diff --git a/.github/actions/bundle/dist/index.js b/.github/actions/bundle/dist/index.js index c863ca69..af69e768 100644 --- a/.github/actions/bundle/dist/index.js +++ b/.github/actions/bundle/dist/index.js @@ -1,7 +1,7 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 545: +/***/ 7936: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -27,8 +27,8 @@ var __importStar = (this && this.__importStar) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.issue = exports.issueCommand = void 0; -const os = __importStar(__nccwpck_require__(87)); -const utils_1 = __nccwpck_require__(403); +const os = __importStar(__nccwpck_require__(2037)); +const utils_1 = __nccwpck_require__(5781); /** * Commands * @@ -100,7 +100,7 @@ function escapeProperty(s) { /***/ }), -/***/ 283: +/***/ 3539: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -135,11 +135,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; -const command_1 = __nccwpck_require__(545); -const file_command_1 = __nccwpck_require__(166); -const utils_1 = __nccwpck_require__(403); -const os = __importStar(__nccwpck_require__(87)); -const path = __importStar(__nccwpck_require__(622)); +const command_1 = __nccwpck_require__(7936); +const file_command_1 = __nccwpck_require__(4041); +const utils_1 = __nccwpck_require__(5781); +const os = __importStar(__nccwpck_require__(2037)); +const path = __importStar(__nccwpck_require__(1017)); /** * The code to exit an action */ @@ -401,7 +401,7 @@ exports.getState = getState; /***/ }), -/***/ 166: +/***/ 4041: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -430,9 +430,9 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.issueCommand = void 0; // We use any as a valid input type /* eslint-disable @typescript-eslint/no-explicit-any */ -const fs = __importStar(__nccwpck_require__(747)); -const os = __importStar(__nccwpck_require__(87)); -const utils_1 = __nccwpck_require__(403); +const fs = __importStar(__nccwpck_require__(7147)); +const os = __importStar(__nccwpck_require__(2037)); +const utils_1 = __nccwpck_require__(5781); function issueCommand(command, message) { const filePath = process.env[`GITHUB_${command}`]; if (!filePath) { @@ -450,7 +450,7 @@ exports.issueCommand = issueCommand; /***/ }), -/***/ 403: +/***/ 5781: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -477,17 +477,17 @@ exports.toCommandValue = toCommandValue; /***/ }), -/***/ 431: +/***/ 9479: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(587) -const path = __nccwpck_require__(622) -const mkdirsSync = __nccwpck_require__(657).mkdirsSync -const utimesMillisSync = __nccwpck_require__(273).utimesMillisSync -const stat = __nccwpck_require__(304) +const fs = __nccwpck_require__(4937) +const path = __nccwpck_require__(1017) +const mkdirsSync = (__nccwpck_require__(4834).mkdirsSync) +const utimesMillisSync = (__nccwpck_require__(5827).utimesMillisSync) +const stat = __nccwpck_require__(7871) function copySync (src, dest, opts) { if (typeof opts === 'function') { @@ -651,31 +651,31 @@ module.exports = copySync /***/ }), -/***/ 269: +/***/ 9337: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; module.exports = { - copySync: __nccwpck_require__(431) + copySync: __nccwpck_require__(9479) } /***/ }), -/***/ 53: +/***/ 6078: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(587) -const path = __nccwpck_require__(622) -const mkdirs = __nccwpck_require__(657).mkdirs -const pathExists = __nccwpck_require__(264).pathExists -const utimesMillis = __nccwpck_require__(273).utimesMillis -const stat = __nccwpck_require__(304) +const fs = __nccwpck_require__(4937) +const path = __nccwpck_require__(1017) +const mkdirs = (__nccwpck_require__(4834).mkdirs) +const pathExists = (__nccwpck_require__(2285).pathExists) +const utimesMillis = (__nccwpck_require__(5827).utimesMillis) +const stat = __nccwpck_require__(7871) function copy (src, dest, opts, cb) { if (typeof opts === 'function' && !cb) { @@ -904,31 +904,31 @@ module.exports = copy /***/ }), -/***/ 311: +/***/ 2233: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromCallback +const u = (__nccwpck_require__(7858).fromCallback) module.exports = { - copy: u(__nccwpck_require__(53)) + copy: u(__nccwpck_require__(6078)) } /***/ }), -/***/ 578: +/***/ 5785: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromPromise -const fs = __nccwpck_require__(632) -const path = __nccwpck_require__(622) -const mkdir = __nccwpck_require__(657) -const remove = __nccwpck_require__(477) +const u = (__nccwpck_require__(7858).fromPromise) +const fs = __nccwpck_require__(8526) +const path = __nccwpck_require__(1017) +const mkdir = __nccwpck_require__(4834) +const remove = __nccwpck_require__(7227) const emptyDir = u(async function emptyDir (dir) { let items @@ -965,16 +965,16 @@ module.exports = { /***/ }), -/***/ 360: +/***/ 2402: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromCallback -const path = __nccwpck_require__(622) -const fs = __nccwpck_require__(587) -const mkdir = __nccwpck_require__(657) +const u = (__nccwpck_require__(7858).fromCallback) +const path = __nccwpck_require__(1017) +const fs = __nccwpck_require__(4937) +const mkdir = __nccwpck_require__(4834) function createFile (file, callback) { function makeFile () { @@ -1042,15 +1042,15 @@ module.exports = { /***/ }), -/***/ 638: +/***/ 308: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const file = __nccwpck_require__(360) -const link = __nccwpck_require__(231) -const symlink = __nccwpck_require__(459) +const file = __nccwpck_require__(2402) +const link = __nccwpck_require__(7129) +const symlink = __nccwpck_require__(3853) module.exports = { // file @@ -1073,18 +1073,18 @@ module.exports = { /***/ }), -/***/ 231: +/***/ 7129: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromCallback -const path = __nccwpck_require__(622) -const fs = __nccwpck_require__(587) -const mkdir = __nccwpck_require__(657) -const pathExists = __nccwpck_require__(264).pathExists -const { areIdentical } = __nccwpck_require__(304) +const u = (__nccwpck_require__(7858).fromCallback) +const path = __nccwpck_require__(1017) +const fs = __nccwpck_require__(4937) +const mkdir = __nccwpck_require__(4834) +const pathExists = (__nccwpck_require__(2285).pathExists) +const { areIdentical } = __nccwpck_require__(7871) function createLink (srcpath, dstpath, callback) { function makeLink (srcpath, dstpath) { @@ -1145,15 +1145,15 @@ module.exports = { /***/ }), -/***/ 861: +/***/ 1302: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const path = __nccwpck_require__(622) -const fs = __nccwpck_require__(587) -const pathExists = __nccwpck_require__(264).pathExists +const path = __nccwpck_require__(1017) +const fs = __nccwpck_require__(4937) +const pathExists = (__nccwpck_require__(2285).pathExists) /** * Function that returns two types of paths, one relative to symlink, and one @@ -1252,13 +1252,13 @@ module.exports = { /***/ }), -/***/ 618: +/***/ 1331: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(587) +const fs = __nccwpck_require__(4937) function symlinkType (srcpath, type, callback) { callback = (typeof type === 'function') ? type : callback @@ -1291,30 +1291,30 @@ module.exports = { /***/ }), -/***/ 459: +/***/ 3853: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromCallback -const path = __nccwpck_require__(622) -const fs = __nccwpck_require__(632) -const _mkdirs = __nccwpck_require__(657) +const u = (__nccwpck_require__(7858).fromCallback) +const path = __nccwpck_require__(1017) +const fs = __nccwpck_require__(8526) +const _mkdirs = __nccwpck_require__(4834) const mkdirs = _mkdirs.mkdirs const mkdirsSync = _mkdirs.mkdirsSync -const _symlinkPaths = __nccwpck_require__(861) +const _symlinkPaths = __nccwpck_require__(1302) const symlinkPaths = _symlinkPaths.symlinkPaths const symlinkPathsSync = _symlinkPaths.symlinkPathsSync -const _symlinkType = __nccwpck_require__(618) +const _symlinkType = __nccwpck_require__(1331) const symlinkType = _symlinkType.symlinkType const symlinkTypeSync = _symlinkType.symlinkTypeSync -const pathExists = __nccwpck_require__(264).pathExists +const pathExists = (__nccwpck_require__(2285).pathExists) -const { areIdentical } = __nccwpck_require__(304) +const { areIdentical } = __nccwpck_require__(7871) function createSymlink (srcpath, dstpath, type, callback) { callback = (typeof type === 'function') ? type : callback @@ -1381,15 +1381,15 @@ module.exports = { /***/ }), -/***/ 632: +/***/ 8526: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { "use strict"; // This is adapted from https://github.com/normalize/mz // Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors -const u = __nccwpck_require__(412).fromCallback -const fs = __nccwpck_require__(587) +const u = (__nccwpck_require__(7858).fromCallback) +const fs = __nccwpck_require__(4937) const api = [ 'access', @@ -1508,7 +1508,7 @@ if (typeof fs.writev === 'function') { /***/ }), -/***/ 372: +/***/ 6417: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -1516,35 +1516,35 @@ if (typeof fs.writev === 'function') { module.exports = { // Export promiseified graceful-fs: - ...__nccwpck_require__(632), + ...__nccwpck_require__(8526), // Export extra methods: - ...__nccwpck_require__(269), - ...__nccwpck_require__(311), - ...__nccwpck_require__(578), - ...__nccwpck_require__(638), - ...__nccwpck_require__(957), - ...__nccwpck_require__(657), - ...__nccwpck_require__(986), - ...__nccwpck_require__(610), - ...__nccwpck_require__(911), - ...__nccwpck_require__(264), - ...__nccwpck_require__(477) + ...__nccwpck_require__(9337), + ...__nccwpck_require__(2233), + ...__nccwpck_require__(5785), + ...__nccwpck_require__(308), + ...__nccwpck_require__(7603), + ...__nccwpck_require__(4834), + ...__nccwpck_require__(5060), + ...__nccwpck_require__(2031), + ...__nccwpck_require__(4019), + ...__nccwpck_require__(2285), + ...__nccwpck_require__(7227) } /***/ }), -/***/ 957: +/***/ 7603: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromPromise -const jsonFile = __nccwpck_require__(5) +const u = (__nccwpck_require__(7858).fromPromise) +const jsonFile = __nccwpck_require__(2489) -jsonFile.outputJson = u(__nccwpck_require__(318)) -jsonFile.outputJsonSync = __nccwpck_require__(54) +jsonFile.outputJson = u(__nccwpck_require__(2179)) +jsonFile.outputJsonSync = __nccwpck_require__(6046) // aliases jsonFile.outputJSON = jsonFile.outputJson jsonFile.outputJSONSync = jsonFile.outputJsonSync @@ -1558,13 +1558,13 @@ module.exports = jsonFile /***/ }), -/***/ 5: +/***/ 2489: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const jsonFile = __nccwpck_require__(576) +const jsonFile = __nccwpck_require__(8429) module.exports = { // jsonfile exports @@ -1577,14 +1577,14 @@ module.exports = { /***/ }), -/***/ 54: +/***/ 6046: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { stringify } = __nccwpck_require__(216) -const { outputFileSync } = __nccwpck_require__(911) +const { stringify } = __nccwpck_require__(8990) +const { outputFileSync } = __nccwpck_require__(4019) function outputJsonSync (file, data, options) { const str = stringify(data, options) @@ -1597,14 +1597,14 @@ module.exports = outputJsonSync /***/ }), -/***/ 318: +/***/ 2179: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const { stringify } = __nccwpck_require__(216) -const { outputFile } = __nccwpck_require__(911) +const { stringify } = __nccwpck_require__(8990) +const { outputFile } = __nccwpck_require__(4019) async function outputJson (file, data, options = {}) { const str = stringify(data, options) @@ -1617,13 +1617,13 @@ module.exports = outputJson /***/ }), -/***/ 657: +/***/ 4834: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromPromise -const { makeDir: _makeDir, makeDirSync } = __nccwpck_require__(896) +const u = (__nccwpck_require__(7858).fromPromise) +const { makeDir: _makeDir, makeDirSync } = __nccwpck_require__(3712) const makeDir = u(_makeDir) module.exports = { @@ -1639,13 +1639,13 @@ module.exports = { /***/ }), -/***/ 896: +/***/ 3712: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(632) -const { checkPath } = __nccwpck_require__(13) +const fs = __nccwpck_require__(8526) +const { checkPath } = __nccwpck_require__(9996) const getMode = options => { const defaults = { mode: 0o777 } @@ -1674,7 +1674,7 @@ module.exports.makeDirSync = (dir, options) => { /***/ }), -/***/ 13: +/***/ 9996: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; @@ -1684,7 +1684,7 @@ module.exports.makeDirSync = (dir, options) => { // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -const path = __nccwpck_require__(622) +const path = __nccwpck_require__(1017) // https://github.com/nodejs/node/issues/8987 // https://github.com/libuv/libuv/pull/1088 @@ -1703,31 +1703,31 @@ module.exports.checkPath = function checkPath (pth) { /***/ }), -/***/ 986: +/***/ 5060: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; module.exports = { - moveSync: __nccwpck_require__(325) + moveSync: __nccwpck_require__(9891) } /***/ }), -/***/ 325: +/***/ 9891: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(587) -const path = __nccwpck_require__(622) -const copySync = __nccwpck_require__(269).copySync -const removeSync = __nccwpck_require__(477).removeSync -const mkdirpSync = __nccwpck_require__(657).mkdirpSync -const stat = __nccwpck_require__(304) +const fs = __nccwpck_require__(4937) +const path = __nccwpck_require__(1017) +const copySync = (__nccwpck_require__(9337).copySync) +const removeSync = (__nccwpck_require__(7227).removeSync) +const mkdirpSync = (__nccwpck_require__(4834).mkdirpSync) +const stat = __nccwpck_require__(7871) function moveSync (src, dest, opts) { opts = opts || {} @@ -1778,33 +1778,33 @@ module.exports = moveSync /***/ }), -/***/ 610: +/***/ 2031: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromCallback +const u = (__nccwpck_require__(7858).fromCallback) module.exports = { - move: u(__nccwpck_require__(914)) + move: u(__nccwpck_require__(7462)) } /***/ }), -/***/ 914: +/***/ 7462: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(587) -const path = __nccwpck_require__(622) -const copy = __nccwpck_require__(311).copy -const remove = __nccwpck_require__(477).remove -const mkdirp = __nccwpck_require__(657).mkdirp -const pathExists = __nccwpck_require__(264).pathExists -const stat = __nccwpck_require__(304) +const fs = __nccwpck_require__(4937) +const path = __nccwpck_require__(1017) +const copy = (__nccwpck_require__(2233).copy) +const remove = (__nccwpck_require__(7227).remove) +const mkdirp = (__nccwpck_require__(4834).mkdirp) +const pathExists = (__nccwpck_require__(2285).pathExists) +const stat = __nccwpck_require__(7871) function move (src, dest, opts, cb) { if (typeof opts === 'function') { @@ -1873,17 +1873,17 @@ module.exports = move /***/ }), -/***/ 911: +/***/ 4019: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromCallback -const fs = __nccwpck_require__(587) -const path = __nccwpck_require__(622) -const mkdir = __nccwpck_require__(657) -const pathExists = __nccwpck_require__(264).pathExists +const u = (__nccwpck_require__(7858).fromCallback) +const fs = __nccwpck_require__(4937) +const path = __nccwpck_require__(1017) +const mkdir = __nccwpck_require__(4834) +const pathExists = (__nccwpck_require__(2285).pathExists) function outputFile (file, data, encoding, callback) { if (typeof encoding === 'function') { @@ -1921,13 +1921,13 @@ module.exports = { /***/ }), -/***/ 264: +/***/ 2285: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const u = __nccwpck_require__(412).fromPromise -const fs = __nccwpck_require__(632) +const u = (__nccwpck_require__(7858).fromPromise) +const fs = __nccwpck_require__(8526) function pathExists (path) { return fs.access(path).then(() => true).catch(() => false) @@ -1941,15 +1941,15 @@ module.exports = { /***/ }), -/***/ 477: +/***/ 7227: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(587) -const u = __nccwpck_require__(412).fromCallback -const rimraf = __nccwpck_require__(583) +const fs = __nccwpck_require__(4937) +const u = (__nccwpck_require__(7858).fromCallback) +const rimraf = __nccwpck_require__(8483) function remove (path, callback) { // Node 14.14.0+ @@ -1971,15 +1971,15 @@ module.exports = { /***/ }), -/***/ 583: +/***/ 8483: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(587) -const path = __nccwpck_require__(622) -const assert = __nccwpck_require__(357) +const fs = __nccwpck_require__(4937) +const path = __nccwpck_require__(1017) +const assert = __nccwpck_require__(9491) const isWindows = (process.platform === 'win32') @@ -2281,15 +2281,15 @@ rimraf.sync = rimrafSync /***/ }), -/***/ 304: +/***/ 7871: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(632) -const path = __nccwpck_require__(622) -const util = __nccwpck_require__(669) +const fs = __nccwpck_require__(8526) +const path = __nccwpck_require__(1017) +const util = __nccwpck_require__(3837) function getStats (src, dest, opts) { const statFunc = opts.dereference @@ -2443,13 +2443,13 @@ module.exports = { /***/ }), -/***/ 273: +/***/ 5827: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { "use strict"; -const fs = __nccwpck_require__(587) +const fs = __nccwpck_require__(4937) function utimesMillis (path, atime, mtime, callback) { // if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback) @@ -2477,7 +2477,7 @@ module.exports = { /***/ }), -/***/ 753: +/***/ 2671: /***/ ((module) => { "use strict"; @@ -2508,15 +2508,15 @@ function clone (obj) { /***/ }), -/***/ 587: +/***/ 8210: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -var fs = __nccwpck_require__(747) -var polyfills = __nccwpck_require__(685) -var legacy = __nccwpck_require__(90) -var clone = __nccwpck_require__(753) +var fs = __nccwpck_require__(7147) +var polyfills = __nccwpck_require__(7181) +var legacy = __nccwpck_require__(6726) +var clone = __nccwpck_require__(2671) -var util = __nccwpck_require__(669) +var util = __nccwpck_require__(3837) /* istanbul ignore next - node 0.x polyfill */ var gracefulQueue @@ -2567,7 +2567,7 @@ if (!fs[gracefulQueue]) { return fs$close.call(fs, fd, function (err) { // This function uses the graceful-fs shared queue if (!err) { - retry() + resetQueue() } if (typeof cb === 'function') @@ -2585,7 +2585,7 @@ if (!fs[gracefulQueue]) { function closeSync (fd) { // This function uses the graceful-fs shared queue fs$closeSync.apply(fs, arguments) - retry() + resetQueue() } Object.defineProperty(closeSync, previousSymbol, { @@ -2597,7 +2597,7 @@ if (!fs[gracefulQueue]) { if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) { process.on('exit', function() { debug(fs[gracefulQueue]) - __nccwpck_require__(357).equal(fs[gracefulQueue].length, 0) + __nccwpck_require__(9491).equal(fs[gracefulQueue].length, 0) }) } } @@ -2627,14 +2627,13 @@ function patch (fs) { return go$readFile(path, options, cb) - function go$readFile (path, options, cb) { + function go$readFile (path, options, cb, startTime) { return fs$readFile(path, options, function (err) { if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) - enqueue([go$readFile, [path, options, cb]]) + enqueue([go$readFile, [path, options, cb], err, startTime || Date.now(), Date.now()]) else { if (typeof cb === 'function') cb.apply(this, arguments) - retry() } }) } @@ -2648,14 +2647,13 @@ function patch (fs) { return go$writeFile(path, data, options, cb) - function go$writeFile (path, data, options, cb) { + function go$writeFile (path, data, options, cb, startTime) { return fs$writeFile(path, data, options, function (err) { if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) - enqueue([go$writeFile, [path, data, options, cb]]) + enqueue([go$writeFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()]) else { if (typeof cb === 'function') cb.apply(this, arguments) - retry() } }) } @@ -2670,14 +2668,13 @@ function patch (fs) { return go$appendFile(path, data, options, cb) - function go$appendFile (path, data, options, cb) { + function go$appendFile (path, data, options, cb, startTime) { return fs$appendFile(path, data, options, function (err) { if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) - enqueue([go$appendFile, [path, data, options, cb]]) + enqueue([go$appendFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()]) else { if (typeof cb === 'function') cb.apply(this, arguments) - retry() } }) } @@ -2691,49 +2688,62 @@ function patch (fs) { cb = flags flags = 0 } - return fs$copyFile(src, dest, flags, function (err) { - if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) - enqueue([fs$copyFile, [src, dest, flags, cb]]) - else { - if (typeof cb === 'function') - cb.apply(this, arguments) - retry() - } - }) + return go$copyFile(src, dest, flags, cb) + + function go$copyFile (src, dest, flags, cb, startTime) { + return fs$copyFile(src, dest, flags, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$copyFile, [src, dest, flags, cb], err, startTime || Date.now(), Date.now()]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + } + }) + } } var fs$readdir = fs.readdir fs.readdir = readdir + var noReaddirOptionVersions = /^v[0-5]\./ function readdir (path, options, cb) { - var args = [path] - if (typeof options !== 'function') { - args.push(options) - } else { - cb = options - } - args.push(go$readdir$cb) + if (typeof options === 'function') + cb = options, options = null - return go$readdir(args) + var go$readdir = noReaddirOptionVersions.test(process.version) + ? function go$readdir (path, options, cb, startTime) { + return fs$readdir(path, fs$readdirCallback( + path, options, cb, startTime + )) + } + : function go$readdir (path, options, cb, startTime) { + return fs$readdir(path, options, fs$readdirCallback( + path, options, cb, startTime + )) + } - function go$readdir$cb (err, files) { - if (files && files.sort) - files.sort() + return go$readdir(path, options, cb) - if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) - enqueue([go$readdir, [args]]) + function fs$readdirCallback (path, options, cb, startTime) { + return function (err, files) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([ + go$readdir, + [path, options, cb], + err, + startTime || Date.now(), + Date.now() + ]) + else { + if (files && files.sort) + files.sort() - else { - if (typeof cb === 'function') - cb.apply(this, arguments) - retry() + if (typeof cb === 'function') + cb.call(this, err, files) + } } } } - function go$readdir (args) { - return fs$readdir.apply(fs, args) - } - if (process.version.substr(0, 4) === 'v0.8') { var legStreams = legacy(fs) ReadStream = legStreams.ReadStream @@ -2856,14 +2866,13 @@ function patch (fs) { return go$open(path, flags, mode, cb) - function go$open (path, flags, mode, cb) { + function go$open (path, flags, mode, cb, startTime) { return fs$open(path, flags, mode, function (err, fd) { if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) - enqueue([go$open, [path, flags, mode, cb]]) + enqueue([go$open, [path, flags, mode, cb], err, startTime || Date.now(), Date.now()]) else { if (typeof cb === 'function') cb.apply(this, arguments) - retry() } }) } @@ -2875,23 +2884,89 @@ function patch (fs) { function enqueue (elem) { debug('ENQUEUE', elem[0].name, elem[1]) fs[gracefulQueue].push(elem) + retry() +} + +// keep track of the timeout between retry() calls +var retryTimer + +// reset the startTime and lastTime to now +// this resets the start of the 60 second overall timeout as well as the +// delay between attempts so that we'll retry these jobs sooner +function resetQueue () { + var now = Date.now() + for (var i = 0; i < fs[gracefulQueue].length; ++i) { + // entries that are only a length of 2 are from an older version, don't + // bother modifying those since they'll be retried anyway. + if (fs[gracefulQueue][i].length > 2) { + fs[gracefulQueue][i][3] = now // startTime + fs[gracefulQueue][i][4] = now // lastTime + } + } + // call retry to make sure we're actively processing the queue + retry() } function retry () { + // clear the timer and remove it to help prevent unintended concurrency + clearTimeout(retryTimer) + retryTimer = undefined + + if (fs[gracefulQueue].length === 0) + return + var elem = fs[gracefulQueue].shift() - if (elem) { - debug('RETRY', elem[0].name, elem[1]) - elem[0].apply(null, elem[1]) + var fn = elem[0] + var args = elem[1] + // these items may be unset if they were added by an older graceful-fs + var err = elem[2] + var startTime = elem[3] + var lastTime = elem[4] + + // if we don't have a startTime we have no way of knowing if we've waited + // long enough, so go ahead and retry this item now + if (startTime === undefined) { + debug('RETRY', fn.name, args) + fn.apply(null, args) + } else if (Date.now() - startTime >= 60000) { + // it's been more than 60 seconds total, bail now + debug('TIMEOUT', fn.name, args) + var cb = args.pop() + if (typeof cb === 'function') + cb.call(null, err) + } else { + // the amount of time between the last attempt and right now + var sinceAttempt = Date.now() - lastTime + // the amount of time between when we first tried, and when we last tried + // rounded up to at least 1 + var sinceStart = Math.max(lastTime - startTime, 1) + // backoff. wait longer than the total time we've been retrying, but only + // up to a maximum of 100ms + var desiredDelay = Math.min(sinceStart * 1.2, 100) + // it's been long enough since the last retry, do it again + if (sinceAttempt >= desiredDelay) { + debug('RETRY', fn.name, args) + fn.apply(null, args.concat([startTime])) + } else { + // if we can't do this job yet, push it to the end of the queue + // and let the next iteration check again + fs[gracefulQueue].push(elem) + } + } + + // schedule our next run if one isn't already scheduled + if (retryTimer === undefined) { + retryTimer = setTimeout(retry, 0) } } /***/ }), -/***/ 90: +/***/ 6726: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -var Stream = __nccwpck_require__(413).Stream +var Stream = (__nccwpck_require__(2781).Stream) module.exports = legacy @@ -3013,10 +3088,10 @@ function legacy (fs) { /***/ }), -/***/ 685: +/***/ 7181: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -var constants = __nccwpck_require__(619) +var constants = __nccwpck_require__(2057) var origCwd = process.cwd var cwd = null @@ -3089,13 +3164,13 @@ function patch (fs) { fs.lstatSync = statFixSync(fs.lstatSync) // if lchmod/lchown do not exist, then make them no-ops - if (!fs.lchmod) { + if (fs.chmod && !fs.lchmod) { fs.lchmod = function (path, mode, cb) { if (cb) process.nextTick(cb) } fs.lchmodSync = function () {} } - if (!fs.lchown) { + if (fs.chown && !fs.lchown) { fs.lchown = function (path, uid, gid, cb) { if (cb) process.nextTick(cb) } @@ -3112,32 +3187,38 @@ function patch (fs) { // CPU to a busy looping process, which can cause the program causing the lock // contention to be starved of CPU by node, so the contention doesn't resolve. if (platform === "win32") { - fs.rename = (function (fs$rename) { return function (from, to, cb) { - var start = Date.now() - var backoff = 0; - fs$rename(from, to, function CB (er) { - if (er - && (er.code === "EACCES" || er.code === "EPERM") - && Date.now() - start < 60000) { - setTimeout(function() { - fs.stat(to, function (stater, st) { - if (stater && stater.code === "ENOENT") - fs$rename(from, to, CB); - else - cb(er) - }) - }, backoff) - if (backoff < 100) - backoff += 10; - return; - } - if (cb) cb(er) - }) - }})(fs.rename) + fs.rename = typeof fs.rename !== 'function' ? fs.rename + : (function (fs$rename) { + function rename (from, to, cb) { + var start = Date.now() + var backoff = 0; + fs$rename(from, to, function CB (er) { + if (er + && (er.code === "EACCES" || er.code === "EPERM") + && Date.now() - start < 60000) { + setTimeout(function() { + fs.stat(to, function (stater, st) { + if (stater && stater.code === "ENOENT") + fs$rename(from, to, CB); + else + cb(er) + }) + }, backoff) + if (backoff < 100) + backoff += 10; + return; + } + if (cb) cb(er) + }) + } + if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename) + return rename + })(fs.rename) } // if read() returns EAGAIN, then just try it again. - fs.read = (function (fs$read) { + fs.read = typeof fs.read !== 'function' ? fs.read + : (function (fs$read) { function read (fd, buffer, offset, length, position, callback_) { var callback if (callback_ && typeof callback_ === 'function') { @@ -3158,7 +3239,8 @@ function patch (fs) { return read })(fs.read) - fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) { + fs.readSync = typeof fs.readSync !== 'function' ? fs.readSync + : (function (fs$readSync) { return function (fd, buffer, offset, length, position) { var eagCounter = 0 while (true) { try { @@ -3217,7 +3299,7 @@ function patch (fs) { } function patchLutimes (fs) { - if (constants.hasOwnProperty("O_SYMLINK")) { + if (constants.hasOwnProperty("O_SYMLINK") && fs.futimes) { fs.lutimes = function (path, at, mt, cb) { fs.open(path, constants.O_SYMLINK, function (er, fd) { if (er) { @@ -3251,7 +3333,7 @@ function patch (fs) { return ret } - } else { + } else if (fs.futimes) { fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) } fs.lutimesSync = function () {} } @@ -3328,8 +3410,10 @@ function patch (fs) { return function (target, options) { var stats = options ? orig.call(fs, target, options) : orig.call(fs, target) - if (stats.uid < 0) stats.uid += 0x100000000 - if (stats.gid < 0) stats.gid += 0x100000000 + if (stats) { + if (stats.uid < 0) stats.uid += 0x100000000 + if (stats.gid < 0) stats.gid += 0x100000000 + } return stats; } } @@ -3366,123 +3450,6984 @@ function patch (fs) { /***/ }), -/***/ 576: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/***/ 598: +/***/ ((module) => { -let _fs -try { - _fs = __nccwpck_require__(587) -} catch (_) { - _fs = __nccwpck_require__(747) -} -const universalify = __nccwpck_require__(412) -const { stringify, stripBom } = __nccwpck_require__(216) +"use strict"; -async function _readFile (file, options = {}) { - if (typeof options === 'string') { - options = { encoding: options } - } - const fs = options.fs || _fs +module.exports = clone - const shouldThrow = 'throws' in options ? options.throws : true +var getPrototypeOf = Object.getPrototypeOf || function (obj) { + return obj.__proto__ +} - let data = await universalify.fromCallback(fs.readFile)(file, options) +function clone (obj) { + if (obj === null || typeof obj !== 'object') + return obj - data = stripBom(data) + if (obj instanceof Object) + var copy = { __proto__: getPrototypeOf(obj) } + else + var copy = Object.create(null) - let obj - try { - obj = JSON.parse(data, options ? options.reviver : null) - } catch (err) { - if (shouldThrow) { - err.message = `${file}: ${err.message}` - throw err - } else { - return null - } - } + Object.getOwnPropertyNames(obj).forEach(function (key) { + Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key)) + }) - return obj + return copy } -const readFile = universalify.fromPromise(_readFile) - -function readFileSync (file, options = {}) { - if (typeof options === 'string') { - options = { encoding: options } - } - const fs = options.fs || _fs +/***/ }), - const shouldThrow = 'throws' in options ? options.throws : true +/***/ 4937: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - try { - let content = fs.readFileSync(file, options) - content = stripBom(content) - return JSON.parse(content, options.reviver) - } catch (err) { - if (shouldThrow) { - err.message = `${file}: ${err.message}` - throw err - } else { - return null - } - } -} +var fs = __nccwpck_require__(7147) +var polyfills = __nccwpck_require__(3164) +var legacy = __nccwpck_require__(5813) +var clone = __nccwpck_require__(598) -async function _writeFile (file, obj, options = {}) { - const fs = options.fs || _fs +var util = __nccwpck_require__(3837) - const str = stringify(obj, options) +/* istanbul ignore next - node 0.x polyfill */ +var gracefulQueue +var previousSymbol - await universalify.fromCallback(fs.writeFile)(file, str, options) +/* istanbul ignore else - node 0.x polyfill */ +if (typeof Symbol === 'function' && typeof Symbol.for === 'function') { + gracefulQueue = Symbol.for('graceful-fs.queue') + // This is used in testing by future versions + previousSymbol = Symbol.for('graceful-fs.previous') +} else { + gracefulQueue = '___graceful-fs.queue' + previousSymbol = '___graceful-fs.previous' } -const writeFile = universalify.fromPromise(_writeFile) - -function writeFileSync (file, obj, options = {}) { - const fs = options.fs || _fs - - const str = stringify(obj, options) - // not sure if fs.writeFileSync returns anything, but just in case - return fs.writeFileSync(file, str, options) -} +function noop () {} -const jsonfile = { - readFile, - readFileSync, - writeFile, - writeFileSync +function publishQueue(context, queue) { + Object.defineProperty(context, gracefulQueue, { + get: function() { + return queue + } + }) } -module.exports = jsonfile +var debug = noop +if (util.debuglog) + debug = util.debuglog('gfs4') +else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) + debug = function() { + var m = util.format.apply(util, arguments) + m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ') + console.error(m) + } +// Once time initialization +if (!fs[gracefulQueue]) { + // This queue can be shared by multiple loaded instances + var queue = global[gracefulQueue] || [] + publishQueue(fs, queue) -/***/ }), + // Patch fs.close/closeSync to shared queue version, because we need + // to retry() whenever a close happens *anywhere* in the program. + // This is essential when multiple graceful-fs instances are + // in play at the same time. + fs.close = (function (fs$close) { + function close (fd, cb) { + return fs$close.call(fs, fd, function (err) { + // This function uses the graceful-fs shared queue + if (!err) { + retry() + } -/***/ 216: -/***/ ((module) => { + if (typeof cb === 'function') + cb.apply(this, arguments) + }) + } -function stringify (obj, { EOL = '\n', finalEOL = true, replacer = null, spaces } = {}) { - const EOF = finalEOL ? EOL : '' - const str = JSON.stringify(obj, replacer, spaces) + Object.defineProperty(close, previousSymbol, { + value: fs$close + }) + return close + })(fs.close) - return str.replace(/\n/g, EOL) + EOF -} + fs.closeSync = (function (fs$closeSync) { + function closeSync (fd) { + // This function uses the graceful-fs shared queue + fs$closeSync.apply(fs, arguments) + retry() + } -function stripBom (content) { - // we do this because JSON.parse would convert it to a utf8 string if encoding wasn't specified - if (Buffer.isBuffer(content)) content = content.toString('utf8') - return content.replace(/^\uFEFF/, '') + Object.defineProperty(closeSync, previousSymbol, { + value: fs$closeSync + }) + return closeSync + })(fs.closeSync) + + if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) { + process.on('exit', function() { + debug(fs[gracefulQueue]) + __nccwpck_require__(9491).equal(fs[gracefulQueue].length, 0) + }) + } +} + +if (!global[gracefulQueue]) { + publishQueue(global, fs[gracefulQueue]); +} + +module.exports = patch(clone(fs)) +if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) { + module.exports = patch(fs) + fs.__patched = true; } -module.exports = { stringify, stripBom } +function patch (fs) { + // Everything that references the open() function needs to be in here + polyfills(fs) + fs.gracefulify = patch + + fs.createReadStream = createReadStream + fs.createWriteStream = createWriteStream + var fs$readFile = fs.readFile + fs.readFile = readFile + function readFile (path, options, cb) { + if (typeof options === 'function') + cb = options, options = null + + return go$readFile(path, options, cb) + + function go$readFile (path, options, cb) { + return fs$readFile(path, options, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$readFile, [path, options, cb]]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + retry() + } + }) + } + } + + var fs$writeFile = fs.writeFile + fs.writeFile = writeFile + function writeFile (path, data, options, cb) { + if (typeof options === 'function') + cb = options, options = null + + return go$writeFile(path, data, options, cb) + + function go$writeFile (path, data, options, cb) { + return fs$writeFile(path, data, options, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$writeFile, [path, data, options, cb]]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + retry() + } + }) + } + } + + var fs$appendFile = fs.appendFile + if (fs$appendFile) + fs.appendFile = appendFile + function appendFile (path, data, options, cb) { + if (typeof options === 'function') + cb = options, options = null + + return go$appendFile(path, data, options, cb) + + function go$appendFile (path, data, options, cb) { + return fs$appendFile(path, data, options, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$appendFile, [path, data, options, cb]]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + retry() + } + }) + } + } + + var fs$copyFile = fs.copyFile + if (fs$copyFile) + fs.copyFile = copyFile + function copyFile (src, dest, flags, cb) { + if (typeof flags === 'function') { + cb = flags + flags = 0 + } + return fs$copyFile(src, dest, flags, function (err) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([fs$copyFile, [src, dest, flags, cb]]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + retry() + } + }) + } + + var fs$readdir = fs.readdir + fs.readdir = readdir + function readdir (path, options, cb) { + var args = [path] + if (typeof options !== 'function') { + args.push(options) + } else { + cb = options + } + args.push(go$readdir$cb) + + return go$readdir(args) + + function go$readdir$cb (err, files) { + if (files && files.sort) + files.sort() + + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$readdir, [args]]) + + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + retry() + } + } + } + + function go$readdir (args) { + return fs$readdir.apply(fs, args) + } + + if (process.version.substr(0, 4) === 'v0.8') { + var legStreams = legacy(fs) + ReadStream = legStreams.ReadStream + WriteStream = legStreams.WriteStream + } + + var fs$ReadStream = fs.ReadStream + if (fs$ReadStream) { + ReadStream.prototype = Object.create(fs$ReadStream.prototype) + ReadStream.prototype.open = ReadStream$open + } + + var fs$WriteStream = fs.WriteStream + if (fs$WriteStream) { + WriteStream.prototype = Object.create(fs$WriteStream.prototype) + WriteStream.prototype.open = WriteStream$open + } + + Object.defineProperty(fs, 'ReadStream', { + get: function () { + return ReadStream + }, + set: function (val) { + ReadStream = val + }, + enumerable: true, + configurable: true + }) + Object.defineProperty(fs, 'WriteStream', { + get: function () { + return WriteStream + }, + set: function (val) { + WriteStream = val + }, + enumerable: true, + configurable: true + }) + + // legacy names + var FileReadStream = ReadStream + Object.defineProperty(fs, 'FileReadStream', { + get: function () { + return FileReadStream + }, + set: function (val) { + FileReadStream = val + }, + enumerable: true, + configurable: true + }) + var FileWriteStream = WriteStream + Object.defineProperty(fs, 'FileWriteStream', { + get: function () { + return FileWriteStream + }, + set: function (val) { + FileWriteStream = val + }, + enumerable: true, + configurable: true + }) + + function ReadStream (path, options) { + if (this instanceof ReadStream) + return fs$ReadStream.apply(this, arguments), this + else + return ReadStream.apply(Object.create(ReadStream.prototype), arguments) + } + + function ReadStream$open () { + var that = this + open(that.path, that.flags, that.mode, function (err, fd) { + if (err) { + if (that.autoClose) + that.destroy() + + that.emit('error', err) + } else { + that.fd = fd + that.emit('open', fd) + that.read() + } + }) + } + + function WriteStream (path, options) { + if (this instanceof WriteStream) + return fs$WriteStream.apply(this, arguments), this + else + return WriteStream.apply(Object.create(WriteStream.prototype), arguments) + } + + function WriteStream$open () { + var that = this + open(that.path, that.flags, that.mode, function (err, fd) { + if (err) { + that.destroy() + that.emit('error', err) + } else { + that.fd = fd + that.emit('open', fd) + } + }) + } + + function createReadStream (path, options) { + return new fs.ReadStream(path, options) + } + + function createWriteStream (path, options) { + return new fs.WriteStream(path, options) + } + + var fs$open = fs.open + fs.open = open + function open (path, flags, mode, cb) { + if (typeof mode === 'function') + cb = mode, mode = null + + return go$open(path, flags, mode, cb) + + function go$open (path, flags, mode, cb) { + return fs$open(path, flags, mode, function (err, fd) { + if (err && (err.code === 'EMFILE' || err.code === 'ENFILE')) + enqueue([go$open, [path, flags, mode, cb]]) + else { + if (typeof cb === 'function') + cb.apply(this, arguments) + retry() + } + }) + } + } + + return fs +} + +function enqueue (elem) { + debug('ENQUEUE', elem[0].name, elem[1]) + fs[gracefulQueue].push(elem) +} + +function retry () { + var elem = fs[gracefulQueue].shift() + if (elem) { + debug('RETRY', elem[0].name, elem[1]) + elem[0].apply(null, elem[1]) + } +} + + +/***/ }), + +/***/ 5813: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +var Stream = (__nccwpck_require__(2781).Stream) + +module.exports = legacy + +function legacy (fs) { + return { + ReadStream: ReadStream, + WriteStream: WriteStream + } + + function ReadStream (path, options) { + if (!(this instanceof ReadStream)) return new ReadStream(path, options); + + Stream.call(this); + + var self = this; + + this.path = path; + this.fd = null; + this.readable = true; + this.paused = false; + + this.flags = 'r'; + this.mode = 438; /*=0666*/ + this.bufferSize = 64 * 1024; + + options = options || {}; + + // Mixin options into this + var keys = Object.keys(options); + for (var index = 0, length = keys.length; index < length; index++) { + var key = keys[index]; + this[key] = options[key]; + } + + if (this.encoding) this.setEncoding(this.encoding); + + if (this.start !== undefined) { + if ('number' !== typeof this.start) { + throw TypeError('start must be a Number'); + } + if (this.end === undefined) { + this.end = Infinity; + } else if ('number' !== typeof this.end) { + throw TypeError('end must be a Number'); + } + + if (this.start > this.end) { + throw new Error('start must be <= end'); + } + + this.pos = this.start; + } + + if (this.fd !== null) { + process.nextTick(function() { + self._read(); + }); + return; + } + + fs.open(this.path, this.flags, this.mode, function (err, fd) { + if (err) { + self.emit('error', err); + self.readable = false; + return; + } + + self.fd = fd; + self.emit('open', fd); + self._read(); + }) + } + + function WriteStream (path, options) { + if (!(this instanceof WriteStream)) return new WriteStream(path, options); + + Stream.call(this); + + this.path = path; + this.fd = null; + this.writable = true; + + this.flags = 'w'; + this.encoding = 'binary'; + this.mode = 438; /*=0666*/ + this.bytesWritten = 0; + + options = options || {}; + + // Mixin options into this + var keys = Object.keys(options); + for (var index = 0, length = keys.length; index < length; index++) { + var key = keys[index]; + this[key] = options[key]; + } + + if (this.start !== undefined) { + if ('number' !== typeof this.start) { + throw TypeError('start must be a Number'); + } + if (this.start < 0) { + throw new Error('start must be >= zero'); + } + + this.pos = this.start; + } + + this.busy = false; + this._queue = []; + + if (this.fd === null) { + this._open = fs.open; + this._queue.push([this._open, this.path, this.flags, this.mode, undefined]); + this.flush(); + } + } +} + + +/***/ }), + +/***/ 3164: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +var constants = __nccwpck_require__(2057) + +var origCwd = process.cwd +var cwd = null + +var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform + +process.cwd = function() { + if (!cwd) + cwd = origCwd.call(process) + return cwd +} +try { + process.cwd() +} catch (er) {} + +// This check is needed until node.js 12 is required +if (typeof process.chdir === 'function') { + var chdir = process.chdir + process.chdir = function (d) { + cwd = null + chdir.call(process, d) + } + if (Object.setPrototypeOf) Object.setPrototypeOf(process.chdir, chdir) +} + +module.exports = patch + +function patch (fs) { + // (re-)implement some things that are known busted or missing. + + // lchmod, broken prior to 0.6.2 + // back-port the fix here. + if (constants.hasOwnProperty('O_SYMLINK') && + process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) { + patchLchmod(fs) + } + + // lutimes implementation, or no-op + if (!fs.lutimes) { + patchLutimes(fs) + } + + // https://github.com/isaacs/node-graceful-fs/issues/4 + // Chown should not fail on einval or eperm if non-root. + // It should not fail on enosys ever, as this just indicates + // that a fs doesn't support the intended operation. + + fs.chown = chownFix(fs.chown) + fs.fchown = chownFix(fs.fchown) + fs.lchown = chownFix(fs.lchown) + + fs.chmod = chmodFix(fs.chmod) + fs.fchmod = chmodFix(fs.fchmod) + fs.lchmod = chmodFix(fs.lchmod) + + fs.chownSync = chownFixSync(fs.chownSync) + fs.fchownSync = chownFixSync(fs.fchownSync) + fs.lchownSync = chownFixSync(fs.lchownSync) + + fs.chmodSync = chmodFixSync(fs.chmodSync) + fs.fchmodSync = chmodFixSync(fs.fchmodSync) + fs.lchmodSync = chmodFixSync(fs.lchmodSync) + + fs.stat = statFix(fs.stat) + fs.fstat = statFix(fs.fstat) + fs.lstat = statFix(fs.lstat) + + fs.statSync = statFixSync(fs.statSync) + fs.fstatSync = statFixSync(fs.fstatSync) + fs.lstatSync = statFixSync(fs.lstatSync) + + // if lchmod/lchown do not exist, then make them no-ops + if (!fs.lchmod) { + fs.lchmod = function (path, mode, cb) { + if (cb) process.nextTick(cb) + } + fs.lchmodSync = function () {} + } + if (!fs.lchown) { + fs.lchown = function (path, uid, gid, cb) { + if (cb) process.nextTick(cb) + } + fs.lchownSync = function () {} + } + + // on Windows, A/V software can lock the directory, causing this + // to fail with an EACCES or EPERM if the directory contains newly + // created files. Try again on failure, for up to 60 seconds. + + // Set the timeout this long because some Windows Anti-Virus, such as Parity + // bit9, may lock files for up to a minute, causing npm package install + // failures. Also, take care to yield the scheduler. Windows scheduling gives + // CPU to a busy looping process, which can cause the program causing the lock + // contention to be starved of CPU by node, so the contention doesn't resolve. + if (platform === "win32") { + fs.rename = (function (fs$rename) { return function (from, to, cb) { + var start = Date.now() + var backoff = 0; + fs$rename(from, to, function CB (er) { + if (er + && (er.code === "EACCES" || er.code === "EPERM") + && Date.now() - start < 60000) { + setTimeout(function() { + fs.stat(to, function (stater, st) { + if (stater && stater.code === "ENOENT") + fs$rename(from, to, CB); + else + cb(er) + }) + }, backoff) + if (backoff < 100) + backoff += 10; + return; + } + if (cb) cb(er) + }) + }})(fs.rename) + } + + // if read() returns EAGAIN, then just try it again. + fs.read = (function (fs$read) { + function read (fd, buffer, offset, length, position, callback_) { + var callback + if (callback_ && typeof callback_ === 'function') { + var eagCounter = 0 + callback = function (er, _, __) { + if (er && er.code === 'EAGAIN' && eagCounter < 10) { + eagCounter ++ + return fs$read.call(fs, fd, buffer, offset, length, position, callback) + } + callback_.apply(this, arguments) + } + } + return fs$read.call(fs, fd, buffer, offset, length, position, callback) + } + + // This ensures `util.promisify` works as it does for native `fs.read`. + if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read) + return read + })(fs.read) + + fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) { + var eagCounter = 0 + while (true) { + try { + return fs$readSync.call(fs, fd, buffer, offset, length, position) + } catch (er) { + if (er.code === 'EAGAIN' && eagCounter < 10) { + eagCounter ++ + continue + } + throw er + } + } + }})(fs.readSync) + + function patchLchmod (fs) { + fs.lchmod = function (path, mode, callback) { + fs.open( path + , constants.O_WRONLY | constants.O_SYMLINK + , mode + , function (err, fd) { + if (err) { + if (callback) callback(err) + return + } + // prefer to return the chmod error, if one occurs, + // but still try to close, and report closing errors if they occur. + fs.fchmod(fd, mode, function (err) { + fs.close(fd, function(err2) { + if (callback) callback(err || err2) + }) + }) + }) + } + + fs.lchmodSync = function (path, mode) { + var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode) + + // prefer to return the chmod error, if one occurs, + // but still try to close, and report closing errors if they occur. + var threw = true + var ret + try { + ret = fs.fchmodSync(fd, mode) + threw = false + } finally { + if (threw) { + try { + fs.closeSync(fd) + } catch (er) {} + } else { + fs.closeSync(fd) + } + } + return ret + } + } + + function patchLutimes (fs) { + if (constants.hasOwnProperty("O_SYMLINK")) { + fs.lutimes = function (path, at, mt, cb) { + fs.open(path, constants.O_SYMLINK, function (er, fd) { + if (er) { + if (cb) cb(er) + return + } + fs.futimes(fd, at, mt, function (er) { + fs.close(fd, function (er2) { + if (cb) cb(er || er2) + }) + }) + }) + } + + fs.lutimesSync = function (path, at, mt) { + var fd = fs.openSync(path, constants.O_SYMLINK) + var ret + var threw = true + try { + ret = fs.futimesSync(fd, at, mt) + threw = false + } finally { + if (threw) { + try { + fs.closeSync(fd) + } catch (er) {} + } else { + fs.closeSync(fd) + } + } + return ret + } + + } else { + fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb) } + fs.lutimesSync = function () {} + } + } + + function chmodFix (orig) { + if (!orig) return orig + return function (target, mode, cb) { + return orig.call(fs, target, mode, function (er) { + if (chownErOk(er)) er = null + if (cb) cb.apply(this, arguments) + }) + } + } + + function chmodFixSync (orig) { + if (!orig) return orig + return function (target, mode) { + try { + return orig.call(fs, target, mode) + } catch (er) { + if (!chownErOk(er)) throw er + } + } + } + + + function chownFix (orig) { + if (!orig) return orig + return function (target, uid, gid, cb) { + return orig.call(fs, target, uid, gid, function (er) { + if (chownErOk(er)) er = null + if (cb) cb.apply(this, arguments) + }) + } + } + + function chownFixSync (orig) { + if (!orig) return orig + return function (target, uid, gid) { + try { + return orig.call(fs, target, uid, gid) + } catch (er) { + if (!chownErOk(er)) throw er + } + } + } + + function statFix (orig) { + if (!orig) return orig + // Older versions of Node erroneously returned signed integers for + // uid + gid. + return function (target, options, cb) { + if (typeof options === 'function') { + cb = options + options = null + } + function callback (er, stats) { + if (stats) { + if (stats.uid < 0) stats.uid += 0x100000000 + if (stats.gid < 0) stats.gid += 0x100000000 + } + if (cb) cb.apply(this, arguments) + } + return options ? orig.call(fs, target, options, callback) + : orig.call(fs, target, callback) + } + } + + function statFixSync (orig) { + if (!orig) return orig + // Older versions of Node erroneously returned signed integers for + // uid + gid. + return function (target, options) { + var stats = options ? orig.call(fs, target, options) + : orig.call(fs, target) + if (stats.uid < 0) stats.uid += 0x100000000 + if (stats.gid < 0) stats.gid += 0x100000000 + return stats; + } + } + + // ENOSYS means that the fs doesn't support the op. Just ignore + // that, because it doesn't matter. + // + // if there's no getuid, or if getuid() is something other + // than 0, and the error is EINVAL or EPERM, then just ignore + // it. + // + // This specific case is a silent failure in cp, install, tar, + // and most other unix tools that manage permissions. + // + // When running as root, or if other types of errors are + // encountered, then it's strict. + function chownErOk (er) { + if (!er) + return true + + if (er.code === "ENOSYS") + return true + + var nonroot = !process.getuid || process.getuid() !== 0 + if (nonroot) { + if (er.code === "EINVAL" || er.code === "EPERM") + return true + } + + return false + } +} + + +/***/ }), + +/***/ 8429: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +let _fs +try { + _fs = __nccwpck_require__(8210) +} catch (_) { + _fs = __nccwpck_require__(7147) +} +const universalify = __nccwpck_require__(7858) +const { stringify, stripBom } = __nccwpck_require__(8990) + +async function _readFile (file, options = {}) { + if (typeof options === 'string') { + options = { encoding: options } + } + + const fs = options.fs || _fs + + const shouldThrow = 'throws' in options ? options.throws : true + + let data = await universalify.fromCallback(fs.readFile)(file, options) + + data = stripBom(data) + + let obj + try { + obj = JSON.parse(data, options ? options.reviver : null) + } catch (err) { + if (shouldThrow) { + err.message = `${file}: ${err.message}` + throw err + } else { + return null + } + } + + return obj +} + +const readFile = universalify.fromPromise(_readFile) + +function readFileSync (file, options = {}) { + if (typeof options === 'string') { + options = { encoding: options } + } + + const fs = options.fs || _fs + + const shouldThrow = 'throws' in options ? options.throws : true + + try { + let content = fs.readFileSync(file, options) + content = stripBom(content) + return JSON.parse(content, options.reviver) + } catch (err) { + if (shouldThrow) { + err.message = `${file}: ${err.message}` + throw err + } else { + return null + } + } +} + +async function _writeFile (file, obj, options = {}) { + const fs = options.fs || _fs + + const str = stringify(obj, options) + + await universalify.fromCallback(fs.writeFile)(file, str, options) +} + +const writeFile = universalify.fromPromise(_writeFile) + +function writeFileSync (file, obj, options = {}) { + const fs = options.fs || _fs + + const str = stringify(obj, options) + // not sure if fs.writeFileSync returns anything, but just in case + return fs.writeFileSync(file, str, options) +} + +const jsonfile = { + readFile, + readFileSync, + writeFile, + writeFileSync +} + +module.exports = jsonfile + + +/***/ }), + +/***/ 8990: +/***/ ((module) => { + +function stringify (obj, { EOL = '\n', finalEOL = true, replacer = null, spaces } = {}) { + const EOF = finalEOL ? EOL : '' + const str = JSON.stringify(obj, replacer, spaces) + + return str.replace(/\n/g, EOL) + EOF +} + +function stripBom (content) { + // we do this because JSON.parse would convert it to a utf8 string if encoding wasn't specified + if (Buffer.isBuffer(content)) content = content.toString('utf8') + return content.replace(/^\uFEFF/, '') +} + +module.exports = { stringify, stripBom } + + +/***/ }), + +/***/ 2902: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +// Traversal is stopped if `callback` returns `true` +function reverseTraverse(node, callback) { + for (const property of Object.values(node)) { + if (property && typeof property === 'object') { + if (property instanceof Array) { + if (property.length > 0 && property[0].type) { + for (let i = property.length - 1; i >= 0; i--) { + if (reverseTraverse(property[i], callback)) { + return true; + } + } + } + } + else if (property.type) { + if (reverseTraverse(property, callback)) { + return true; + } + } + } + } + return callback(node) || false; +} +exports.reverseTraverse = reverseTraverse; +function isRequireCall(node) { + return (node.type === 'CallExpression' || node.type === 'StringCallExpression') + && node.base.type === 'Identifier' + && node.base.name === 'require' + && (node.type === 'StringCallExpression' || node.arguments.length === 1); +} +function reverseTraverseRequires(node, callback) { + reverseTraverse(node, node => { + return isRequireCall(node) && callback(node) === true; + }); +} +exports.reverseTraverseRequires = reverseTraverseRequires; +function isModuleRegistration(expression, registerIdentifier) { + return expression.type === 'CallExpression' + && expression.arguments.length === 2 + && expression.base.type === 'Identifier' + && expression.base.name === registerIdentifier; +} +function iterateModuleRegistrations(chunk, registerIdentifier, callback) { + const statementCount = chunk.body.length; + for (let i = 0; i < statementCount; i++) { + const node = chunk.body[i]; + if (node.type === 'CallStatement') { + if (isModuleRegistration(node.expression, registerIdentifier)) { + const nameArgument = node.expression.arguments[0]; + const bodyArgument = node.expression.arguments[1]; + if (nameArgument.type === 'StringLiteral' && bodyArgument.type === 'FunctionDeclaration') { + if (callback(nameArgument.value, bodyArgument)) { + return; + } + } + } + } + } +} +exports.iterateModuleRegistrations = iterateModuleRegistrations; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 2656: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +const fs_1 = __nccwpck_require__(7147); +const path_1 = __nccwpck_require__(1017); +const options_1 = __nccwpck_require__(4429); +const process_1 = __nccwpck_require__(1489); +const metadata_1 = __nccwpck_require__(476); +function mergeOptions(options) { + return Object.assign(Object.assign(Object.assign({}, options_1.defaultOptions), options), { identifiers: Object.assign(Object.assign({}, options_1.defaultOptions.identifiers), options.identifiers) }); +} +function bundleModule(module, options) { + const postprocessedContent = options.postprocess ? options.postprocess(module, options) : module.content; + const identifiers = options.identifiers; + return `${identifiers.register}("${module.name}", function(require, _LOADED, ${identifiers.register}, ${identifiers.modules})\n${postprocessedContent}\nend)\n`; +} +function bundleString(lua, options = {}) { + const realizedOptions = mergeOptions(options); + const processedModules = {}; + process_1.processModule({ + name: realizedOptions.rootModuleName, + content: lua, + }, realizedOptions, processedModules); + if (Object.keys(processedModules).length === 1 && !realizedOptions.force) { + return lua; + } + const identifiers = realizedOptions.identifiers; + const runtime = fs_1.readFileSync(__nccwpck_require__.ab + "runtime.lua"); + let bundle = ''; + if (realizedOptions.metadata) { + bundle += metadata_1.generateMetadata(realizedOptions); + } + bundle += `local ${identifiers.require}, ${identifiers.loaded}, ${identifiers.register}, ${identifiers.modules} = ${runtime}`; + bundle += realizedOptions.isolate ? '(nil)\n' : '(require)\n'; + for (const [name, processedModule] of Object.entries(processedModules)) { + bundle += bundleModule({ + name, + content: processedModule.content + }, realizedOptions); + } + bundle += 'return ' + identifiers.require + '("' + realizedOptions.rootModuleName + '")'; + return bundle; +} +exports.bundleString = bundleString; +function bundle(inputFilePath, options = {}) { + const lua = fs_1.readFileSync(inputFilePath, 'utf8'); + return bundleString(lua, options); +} +exports.bundle = bundle; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 4429: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.defaultOptions = { + force: false, + identifiers: { + register: '__bundle_register', + require: '__bundle_require', + loaded: '__bundle_loaded', + modules: '__bundle_modules', + }, + isolate: false, + luaVersion: '5.3', + metadata: true, + paths: ['?', '?.lua'], + rootModuleName: '__root', +}; +//# sourceMappingURL=options.js.map + +/***/ }), + +/***/ 1489: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const fs_1 = __nccwpck_require__(7147); +const path_1 = __nccwpck_require__(1017); +const moonsharp_luaparse_1 = __nccwpck_require__(1418); +const ast_1 = __nccwpck_require__(2902); +const metadata_1 = __nccwpck_require__(476); +const ModuleBundlingError_1 = __importDefault(__nccwpck_require__(2750)); +const ModuleResolutionError_1 = __importDefault(__nccwpck_require__(7151)); +function resolveModule(name, packagePaths) { + const platformName = name.replace(/\./g, path_1.sep); + for (const pattern of packagePaths) { + const path = pattern.replace(/\?/g, platformName); + if (fs_1.existsSync(path) && fs_1.lstatSync(path).isFile()) { + return path; + } + } + return null; +} +exports.resolveModule = resolveModule; +function processModule(module, options, processedModules) { + let content = options.preprocess ? options.preprocess(module, options) : module.content; + const resolvedModules = []; + // Ensure we don't attempt to load modules required in nested bundles + if (!metadata_1.readMetadata(content)) { + let ast = moonsharp_luaparse_1.parse(content, { + locations: true, + luaVersion: options.luaVersion, + ranges: true, + }); + ast_1.reverseTraverseRequires(ast, expression => { + var _a; + const argument = expression.argument || expression.arguments[0]; + let required = null; + if (argument.type == 'StringLiteral') { + required = argument.value; + } + else if (options.expressionHandler) { + required = options.expressionHandler(module, argument); + } + if (required) { + const requiredModuleNames = Array.isArray(required) ? required : [required]; + for (const requiredModule of requiredModuleNames) { + const resolvedPath = resolveModule(requiredModule, options.paths); + if (!resolvedPath) { + const start = (_a = expression.loc) === null || _a === void 0 ? void 0 : _a.start; + throw new ModuleResolutionError_1.default(requiredModule, module.name, start.line, start.column); + } + resolvedModules.push({ + name: requiredModule, + resolvedPath, + }); + } + if (typeof required === "string") { + const range = expression.range; + const baseRange = expression.base.range; + content = content.slice(0, baseRange[1]) + '("' + required + '")' + content.slice(range[1]); + } + } + }); + } + processedModules[module.name] = Object.assign(Object.assign({}, module), { content }); + for (const resolvedModule of resolvedModules) { + if (processedModules[resolvedModule.name]) { + continue; + } + try { + const moduleContent = fs_1.readFileSync(resolvedModule.resolvedPath, 'utf8'); + processModule(Object.assign(Object.assign({}, resolvedModule), { content: moduleContent }), options, processedModules); + } + catch (e) { + throw new ModuleBundlingError_1.default(resolvedModule.name, e); + } + } +} +exports.processModule = processModule; +//# sourceMappingURL=process.js.map + +/***/ }), + +/***/ 2880: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +class MalformedBundleError extends Error { + constructor(message) { + super(message); + } +} +exports["default"] = MalformedBundleError; +//# sourceMappingURL=MalformedBundleError.js.map + +/***/ }), + +/***/ 2750: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +class ModuleBundlingError extends Error { + constructor(moduleName, cause) { + super(`Failed to bundle resolved module ${moduleName}`); + this.moduleName = moduleName; + this.cause = cause; + } +} +exports["default"] = ModuleBundlingError; +//# sourceMappingURL=ModuleBundlingError.js.map + +/***/ }), + +/***/ 7151: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +class ModuleResolutionError extends Error { + constructor(moduleName, parentModuleName, line, column) { + super(`Could not resolve module '${moduleName}' required by '${parentModuleName}' at ${line}:${column}`); + this.moduleName = moduleName; + this.parentModuleName = parentModuleName; + this.line = line; + this.column = column; + } +} +exports["default"] = ModuleResolutionError; +//# sourceMappingURL=ModuleResolutionError.js.map + +/***/ }), + +/***/ 2232: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +class NoBundleMetadataError extends Error { + constructor() { + super("No metadata found. Only bundles with metadata may be unbundled"); + } +} +exports["default"] = NoBundleMetadataError; +//# sourceMappingURL=NoBundleMetadataError.js.map + +/***/ }), + +/***/ 1054: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +var bundle_1 = __nccwpck_require__(2656); +exports.bundle = bundle_1.bundle; +exports.bundleString = bundle_1.bundleString; +var unbundle_1 = __nccwpck_require__(3291); +exports.unbundle = unbundle_1.unbundle; +exports.unbundleString = unbundle_1.unbundleString; +const bundle_2 = __nccwpck_require__(2656); +const unbundle_2 = __nccwpck_require__(3291); +exports["default"] = { + bundle: bundle_2.bundle, + bundleString: bundle_2.bundleString, + unbundle: unbundle_2.unbundle, + unbundleString: unbundle_2.unbundleString, +}; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 476: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const package_json_1 = __nccwpck_require__(4131); +const options_1 = __nccwpck_require__(4429); +const MalformedBundleError_1 = __importDefault(__nccwpck_require__(2880)); +exports.defaultMetadata = { + identifiers: options_1.defaultOptions.identifiers, + luaVersion: options_1.defaultOptions.luaVersion, + rootModuleName: options_1.defaultOptions.rootModuleName, + version: package_json_1.version +}; +function intersectionDiff(a, subset) { + const diff = {}; + for (const key of Object.keys(subset)) { + const setValue = a[key]; + const subsetValue = subset[key]; + if (setValue !== undefined && subsetValue !== undefined && setValue !== subsetValue) { + if (setValue + && typeof setValue === 'object' + && !(setValue instanceof Array) + && subsetValue + && typeof subsetValue === 'object' + && !(subsetValue instanceof Array)) { + const recursiveDiff = intersectionDiff(setValue, subsetValue); + if (Object.entries(recursiveDiff).length > 0) { + diff[key] = recursiveDiff; + } + } + else { + diff[key] = subsetValue; + } + } + } + return diff; +} +function generateMetadata(options) { + const metadata = intersectionDiff(exports.defaultMetadata, options); + metadata.version = package_json_1.version; + return `-- Bundled by luabundle ${JSON.stringify(metadata)}\n`; +} +exports.generateMetadata = generateMetadata; +function parseMetadata(line) { + const match = line.match(/^-- Bundled by luabundle ({.+})$/); + if (match) { + const metadata = JSON.parse(match[1]); + if (!metadata['version']) { + throw new MalformedBundleError_1.default('Bundle contains invalid metadata'); + } + return metadata; + } + return null; +} +function readMetadata(lua) { + // We'll allow users to inject comments and blank lines above our header, but that's it (no code). + for (let [start, end] = [0, lua.indexOf('\n')]; end !== -1; start = end + 1, end = lua.indexOf('\n', start)) { + const line = lua.substring(start, end); + if (line.length > 0 && !line.startsWith("--")) { + break; + } + const metadata = parseMetadata(line); + if (metadata) { + return metadata; + } + } + return null; +} +exports.readMetadata = readMetadata; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 3291: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const fs_1 = __nccwpck_require__(7147); +const metadata_1 = __nccwpck_require__(476); +const process_1 = __nccwpck_require__(4985); +const MalformedBundleError_1 = __importDefault(__nccwpck_require__(2880)); +const NoBundleMetadataError_1 = __importDefault(__nccwpck_require__(2232)); +const defaultOptions = { + rootOnly: false, +}; +function mergeOptions(options) { + return Object.assign(Object.assign({}, defaultOptions), options); +} +function mergeMetadata(metadata) { + return Object.assign(Object.assign(Object.assign({}, metadata_1.defaultMetadata), metadata), { identifiers: Object.assign(Object.assign({}, metadata_1.defaultMetadata.identifiers), metadata.identifiers) }); +} +function unbundleString(lua, options = {}) { + const metadata = metadata_1.readMetadata(lua); + if (!metadata) { + throw new NoBundleMetadataError_1.default(); + } + const realizedOptions = mergeOptions(options); + const realizedMetadata = mergeMetadata(metadata); + const modules = process_1.processModules(lua, realizedMetadata, realizedOptions); + const rootModule = modules[realizedMetadata.rootModuleName]; + if (!rootModule) { + throw new MalformedBundleError_1.default(`Root module '${realizedMetadata.rootModuleName}' not found.`); + } + return { + metadata: realizedMetadata, + modules, + }; +} +exports.unbundleString = unbundleString; +function unbundle(inputFilePath, options = {}) { + const lua = fs_1.readFileSync(inputFilePath, 'utf8'); + return unbundleString(lua, options); +} +exports.unbundle = unbundle; +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 4985: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const moonsharp_luaparse_1 = __nccwpck_require__(1418); +const ast_1 = __nccwpck_require__(2902); +const MalformedBundleError_1 = __importDefault(__nccwpck_require__(2880)); +function extractModule(lua, name, declaration) { + if (declaration.parameters.length !== 4) { + throw new MalformedBundleError_1.default('Module function declaration contained unexpected number of parameters.'); + } + // luaparse does not included comments in the body, even if you enable + // comment parsing. However, we don't want to remove user's comments, + // thus... + const startIndex = declaration.parameters[3].range[1] + ')\n'.length; + const endIndex = declaration.range[1] - '\nend'.length; + const content = lua.substring(startIndex, endIndex); + return { + name, + content, + start: { + index: startIndex, + line: declaration.loc.start.line + 1, + column: 0, + }, + end: { + index: endIndex, + line: declaration.loc.end.line - 1, + column: content.length - content.lastIndexOf('\n') - 1 + } + }; +} +function processModules(lua, metadata, options) { + const modules = {}; + const ast = moonsharp_luaparse_1.parse(lua, { + locations: true, + luaVersion: metadata.luaVersion, + ranges: true, + }); + ast_1.iterateModuleRegistrations(ast, metadata.identifiers.register, (name, body) => { + if (options.rootOnly && name !== metadata.rootModuleName) { + return; + } + modules[name] = extractModule(lua, name, body); + if (options.rootOnly) { + return true; + } + }); + return modules; +} +exports.processModules = processModules; +//# sourceMappingURL=process.js.map + +/***/ }), + +/***/ 4825: +/***/ (function(module, exports, __nccwpck_require__) { + +/* module decorator */ module = __nccwpck_require__.nmd(module); +/*! https://mths.be/luamin v1.0.4 by @mathias */ +;(function(root) { + + // Detect free variables `exports` + var freeExports = true && exports; + + // Detect free variable `module` + var freeModule = true && module && + module.exports == freeExports && module; + + // Detect free variable `global`, from Node.js or Browserified code, + // and use it as `root` + var freeGlobal = typeof global == 'object' && global; + if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) { + root = freeGlobal; + } + + /*--------------------------------------------------------------------------*/ + + var luaparse = root.luaparse || __nccwpck_require__(1021); + luaparse.defaultOptions.comments = false; + luaparse.defaultOptions.scope = true; + var parse = luaparse.parse; + + var regexAlphaUnderscore = /[a-zA-Z_]/; + var regexAlphaNumUnderscore = /[a-zA-Z0-9_]/; + var regexDigits = /[0-9]/; + + // http://www.lua.org/manual/5.2/manual.html#3.4.7 + // http://www.lua.org/source/5.2/lparser.c.html#priority + var PRECEDENCE = { + 'or': 1, + 'and': 2, + '<': 3, '>': 3, '<=': 3, '>=': 3, '~=': 3, '==': 3, + '..': 5, + '+': 6, '-': 6, // binary - + '*': 7, '/': 7, '%': 7, + 'unarynot': 8, 'unary#': 8, 'unary-': 8, // unary - + '^': 10 + }; + + var IDENTIFIER_PARTS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', + 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '_']; + var IDENTIFIER_PARTS_MAX = IDENTIFIER_PARTS.length - 1; + + var each = function(array, fn) { + var index = -1; + var length = array.length; + var max = length - 1; + while (++index < length) { + fn(array[index], index < max); + } + }; + + var indexOf = function(array, value) { + var index = -1; + var length = array.length; + while (++index < length) { + if (array[index] == value) { + return index; + } + } + }; + + var hasOwnProperty = {}.hasOwnProperty; + var extend = function(destination, source) { + var key; + if (source) { + for (key in source) { + if (hasOwnProperty.call(source, key)) { + destination[key] = source[key]; + } + } + } + return destination; + }; + + var generateZeroes = function(length) { + var zero = '0'; + var result = ''; + if (length < 1) { + return result; + } + if (length == 1) { + return zero; + } + while (length) { + if (length & 1) { + result += zero; + } + if (length >>= 1) { + zero += zero; + } + } + return result; + }; + + // http://www.lua.org/manual/5.2/manual.html#3.1 + function isKeyword(id) { + switch (id.length) { + case 2: + return 'do' == id || 'if' == id || 'in' == id || 'or' == id; + case 3: + return 'and' == id || 'end' == id || 'for' == id || 'nil' == id || + 'not' == id; + case 4: + return 'else' == id || 'goto' == id || 'then' == id || 'true' == id; + case 5: + return 'break' == id || 'false' == id || 'local' == id || + 'until' == id || 'while' == id; + case 6: + return 'elseif' == id || 'repeat' == id || 'return' == id; + case 8: + return 'function' == id; + } + return false; + } + + var currentIdentifier; + var identifierMap; + var identifiersInUse; + var generateIdentifier = function(originalName) { + // Preserve `self` in methods + if (originalName == 'self') { + return originalName; + } + + if (hasOwnProperty.call(identifierMap, originalName)) { + return identifierMap[originalName]; + } + var length = currentIdentifier.length; + var position = length - 1; + var character; + var index; + while (position >= 0) { + character = currentIdentifier.charAt(position); + index = indexOf(IDENTIFIER_PARTS, character); + if (index != IDENTIFIER_PARTS_MAX) { + currentIdentifier = currentIdentifier.substring(0, position) + + IDENTIFIER_PARTS[index + 1] + generateZeroes(length - (position + 1)); + if ( + isKeyword(currentIdentifier) || + indexOf(identifiersInUse, currentIdentifier) > -1 + ) { + return generateIdentifier(originalName); + } + identifierMap[originalName] = currentIdentifier; + return currentIdentifier; + } + --position; + } + currentIdentifier = 'a' + generateZeroes(length); + if (indexOf(identifiersInUse, currentIdentifier) > -1) { + return generateIdentifier(originalName); + } + identifierMap[originalName] = currentIdentifier; + return currentIdentifier; + }; + + /*--------------------------------------------------------------------------*/ + + var joinStatements = function(a, b, separator) { + separator || (separator = ' '); + + var lastCharA = a.slice(-1); + var firstCharB = b.charAt(0); + + if (lastCharA == '' || firstCharB == '') { + return a + b; + } + if (regexAlphaUnderscore.test(lastCharA)) { + if (regexAlphaNumUnderscore.test(firstCharB)) { + // e.g. `while` + `1` + // e.g. `local a` + `local b` + return a + separator + b; + } else { + // e.g. `not` + `(2>3 or 3<2)` + // e.g. `x` + `^` + return a + b; + } + } + if (regexDigits.test(lastCharA)) { + if ( + firstCharB == '(' || + !(firstCharB == '.' || + regexAlphaUnderscore.test(firstCharB)) + ) { + // e.g. `1` + `+` + // e.g. `1` + `==` + return a + b; + } else { + // e.g. `1` + `..` + // e.g. `1` + `and` + return a + separator + b; + } + } + if (lastCharA == firstCharB && lastCharA == '-') { + // e.g. `1-` + `-2` + return a + separator + b; + } + return a + b; + }; + + var formatBase = function(base) { + var result = ''; + var type = base.type; + var needsParens = base.inParens && ( + type == 'BinaryExpression' || + type == 'FunctionDeclaration' || + type == 'TableConstructorExpression' || + type == 'LogicalExpression' || + type == 'StringLiteral' + ); + if (needsParens) { + result += '('; + } + result += formatExpression(base); + if (needsParens) { + result += ')'; + } + return result; + }; + + var formatExpression = function(expression, options) { + + options = extend({ + 'precedence': 0, + 'preserveIdentifiers': false + }, options); + + var result = ''; + var currentPrecedence; + var associativity; + var operator; + + var expressionType = expression.type; + + if (expressionType == 'Identifier') { + + result = expression.isLocal && !options.preserveIdentifiers + ? generateIdentifier(expression.name) + : expression.name; + + } else if ( + expressionType == 'StringLiteral' || + expressionType == 'NumericLiteral' || + expressionType == 'BooleanLiteral' || + expressionType == 'NilLiteral' || + expressionType == 'VarargLiteral' + ) { + + result = expression.raw; + + } else if ( + expressionType == 'LogicalExpression' || + expressionType == 'BinaryExpression' + ) { + + // If an expression with precedence x + // contains an expression with precedence < x, + // the inner expression must be wrapped in parens. + operator = expression.operator; + currentPrecedence = PRECEDENCE[operator]; + associativity = 'left'; + + result = formatExpression(expression.left, { + 'precedence': currentPrecedence, + 'direction': 'left', + 'parent': operator + }); + result = joinStatements(result, operator); + result = joinStatements(result, formatExpression(expression.right, { + 'precedence': currentPrecedence, + 'direction': 'right', + 'parent': operator + })); + + if (operator == '^' || operator == '..') { + associativity = "right"; + } + + if ( + currentPrecedence < options.precedence || + ( + currentPrecedence == options.precedence && + associativity != options.direction && + options.parent != '+' && + !(options.parent == '*' && (operator == '/' || operator == '*')) + ) + ) { + // The most simple case here is that of + // protecting the parentheses on the RHS of + // `1 - (2 - 3)` but deleting them from `(1 - 2) - 3`. + // This is generally the right thing to do. The + // semantics of `+` are special however: `1 + (2 - 3)` + // == `1 + 2 - 3`. `-` and `+` are the only two operators + // who share their precedence level. `*` also can + // commute in such a way with `/`, but not with `%` + // (all three share a precedence). So we test for + // all of these conditions and avoid emitting + // parentheses in the cases where we don’t have to. + result = '(' + result + ')'; + } + + } else if (expressionType == 'UnaryExpression') { + + operator = expression.operator; + currentPrecedence = PRECEDENCE['unary' + operator]; + + result = joinStatements( + operator, + formatExpression(expression.argument, { + 'precedence': currentPrecedence + }) + ); + + if ( + currentPrecedence < options.precedence && + // In principle, we should parenthesize the RHS of an + // expression like `3^-2`, because `^` has higher precedence + // than unary `-` according to the manual. But that is + // misleading on the RHS of `^`, since the parser will + // always try to find a unary operator regardless of + // precedence. + !( + (options.parent == '^') && + options.direction == 'right' + ) + ) { + result = '(' + result + ')'; + } + + } else if (expressionType == 'CallExpression') { + + result = formatBase(expression.base) + '('; + + each(expression.arguments, function(argument, needsComma) { + result += formatExpression(argument); + if (needsComma) { + result += ','; + } + }); + result += ')'; + + } else if (expressionType == 'TableCallExpression') { + + result = formatExpression(expression.base) + + formatExpression(expression.arguments); + + } else if (expressionType == 'StringCallExpression') { + + result = formatExpression(expression.base) + + formatExpression(expression.argument); + + } else if (expressionType == 'IndexExpression') { + + result = formatBase(expression.base) + '[' + + formatExpression(expression.index) + ']'; + + } else if (expressionType == 'MemberExpression') { + + result = formatBase(expression.base) + expression.indexer + + formatExpression(expression.identifier, { + 'preserveIdentifiers': true + }); + + } else if (expressionType == 'FunctionDeclaration') { + + result = 'function('; + if (expression.parameters.length) { + each(expression.parameters, function(parameter, needsComma) { + // `Identifier`s have a `name`, `VarargLiteral`s have a `value` + result += parameter.name + ? generateIdentifier(parameter.name) + : parameter.value; + if (needsComma) { + result += ','; + } + }); + } + result += ')'; + result = joinStatements(result, formatStatementList(expression.body)); + result = joinStatements(result, 'end'); + + } else if (expressionType == 'TableConstructorExpression') { + + result = '{'; + + each(expression.fields, function(field, needsComma) { + if (field.type == 'TableKey') { + result += '[' + formatExpression(field.key) + ']=' + + formatExpression(field.value); + } else if (field.type == 'TableValue') { + result += formatExpression(field.value); + } else { // at this point, `field.type == 'TableKeyString'` + result += formatExpression(field.key, { + // TODO: keep track of nested scopes (#18) + 'preserveIdentifiers': true + }) + '=' + formatExpression(field.value); + } + if (needsComma) { + result += ','; + } + }); + + result += '}'; + + } else { + + throw TypeError('Unknown expression type: `' + expressionType + '`'); + + } + + return result; + }; + + var formatStatementList = function(body) { + var result = ''; + each(body, function(statement) { + result = joinStatements(result, formatStatement(statement), ';'); + }); + return result; + }; + + var formatStatement = function(statement) { + var result = ''; + var statementType = statement.type; + + if (statementType == 'AssignmentStatement') { + + // left-hand side + each(statement.variables, function(variable, needsComma) { + result += formatExpression(variable); + if (needsComma) { + result += ','; + } + }); + + // right-hand side + result += '='; + each(statement.init, function(init, needsComma) { + result += formatExpression(init); + if (needsComma) { + result += ','; + } + }); + + } else if (statementType == 'LocalStatement') { + + result = 'local '; + + // left-hand side + each(statement.variables, function(variable, needsComma) { + // Variables in a `LocalStatement` are always local, duh + result += generateIdentifier(variable.name); + if (needsComma) { + result += ','; + } + }); + + // right-hand side + if (statement.init.length) { + result += '='; + each(statement.init, function(init, needsComma) { + result += formatExpression(init); + if (needsComma) { + result += ','; + } + }); + } + + } else if (statementType == 'CallStatement') { + + result = formatExpression(statement.expression); + + } else if (statementType == 'IfStatement') { + + result = joinStatements( + 'if', + formatExpression(statement.clauses[0].condition) + ); + result = joinStatements(result, 'then'); + result = joinStatements( + result, + formatStatementList(statement.clauses[0].body) + ); + each(statement.clauses.slice(1), function(clause) { + if (clause.condition) { + result = joinStatements(result, 'elseif'); + result = joinStatements(result, formatExpression(clause.condition)); + result = joinStatements(result, 'then'); + } else { + result = joinStatements(result, 'else'); + } + result = joinStatements(result, formatStatementList(clause.body)); + }); + result = joinStatements(result, 'end'); + + } else if (statementType == 'WhileStatement') { + + result = joinStatements('while', formatExpression(statement.condition)); + result = joinStatements(result, 'do'); + result = joinStatements(result, formatStatementList(statement.body)); + result = joinStatements(result, 'end'); + + } else if (statementType == 'DoStatement') { + + result = joinStatements('do', formatStatementList(statement.body)); + result = joinStatements(result, 'end'); + + } else if (statementType == 'ReturnStatement') { + + result = 'return'; + + each(statement.arguments, function(argument, needsComma) { + result = joinStatements(result, formatExpression(argument)); + if (needsComma) { + result += ','; + } + }); + + } else if (statementType == 'BreakStatement') { + + result = 'break'; + + } else if (statementType == 'RepeatStatement') { + + result = joinStatements('repeat', formatStatementList(statement.body)); + result = joinStatements(result, 'until'); + result = joinStatements(result, formatExpression(statement.condition)) + + } else if (statementType == 'FunctionDeclaration') { + + result = (statement.isLocal ? 'local ' : '') + 'function '; + result += formatExpression(statement.identifier); + result += '('; + + if (statement.parameters.length) { + each(statement.parameters, function(parameter, needsComma) { + // `Identifier`s have a `name`, `VarargLiteral`s have a `value` + result += parameter.name + ? generateIdentifier(parameter.name) + : parameter.value; + if (needsComma) { + result += ','; + } + }); + } + + result += ')'; + result = joinStatements(result, formatStatementList(statement.body)); + result = joinStatements(result, 'end'); + + } else if (statementType == 'ForGenericStatement') { + // see also `ForNumericStatement` + + result = 'for '; + + each(statement.variables, function(variable, needsComma) { + // The variables in a `ForGenericStatement` are always local + result += generateIdentifier(variable.name); + if (needsComma) { + result += ','; + } + }); + + result += ' in'; + + each(statement.iterators, function(iterator, needsComma) { + result = joinStatements(result, formatExpression(iterator)); + if (needsComma) { + result += ','; + } + }); + + result = joinStatements(result, 'do'); + result = joinStatements(result, formatStatementList(statement.body)); + result = joinStatements(result, 'end'); + + } else if (statementType == 'ForNumericStatement') { + + // The variables in a `ForNumericStatement` are always local + result = 'for ' + generateIdentifier(statement.variable.name) + '='; + result += formatExpression(statement.start) + ',' + + formatExpression(statement.end); + + if (statement.step) { + result += ',' + formatExpression(statement.step); + } + + result = joinStatements(result, 'do'); + result = joinStatements(result, formatStatementList(statement.body)); + result = joinStatements(result, 'end'); + + } else if (statementType == 'LabelStatement') { + + // The identifier names in a `LabelStatement` can safely be renamed + result = '::' + generateIdentifier(statement.label.name) + '::'; + + } else if (statementType == 'GotoStatement') { + + // The identifier names in a `GotoStatement` can safely be renamed + result = 'goto ' + generateIdentifier(statement.label.name); + + } else { + + throw TypeError('Unknown statement type: `' + statementType + '`'); + + } + + return result; + }; + + var minify = function(argument) { + // `argument` can be a Lua code snippet (string) + // or a luaparse-compatible AST (object) + var ast = typeof argument == 'string' + ? parse(argument) + : argument; + + // (Re)set temporary identifier values + identifierMap = {}; + identifiersInUse = []; + // This is a shortcut to help generate the first identifier (`a`) faster + currentIdentifier = '9'; + + // Make sure global variable names aren't renamed + if (ast.globals) { + each(ast.globals, function(object) { + var name = object.name; + identifierMap[name] = name; + identifiersInUse.push(name); + }); + } else { + throw Error('Missing required AST property: `globals`'); + } + + return formatStatementList(ast.body); + }; + + /*--------------------------------------------------------------------------*/ + + var luamin = { + 'version': '1.0.4', + 'minify': minify + }; + + // Some AMD build optimizers, like r.js, check for specific condition patterns + // like the following: + if ( + typeof define == 'function' && + typeof define.amd == 'object' && + define.amd + ) { + define(function() { + return luamin; + }); + } else if (freeExports && !freeExports.nodeType) { + if (freeModule) { // in Node.js or RingoJS v0.8.0+ + freeModule.exports = luamin; + } else { // in Narwhal or RingoJS v0.7.0- + extend(freeExports, luamin); + } + } else { // in Rhino or a web browser + root.luamin = luamin; + } + +}(this)); + + +/***/ }), + +/***/ 1021: +/***/ (function(module, exports, __nccwpck_require__) { + +/* module decorator */ module = __nccwpck_require__.nmd(module); +/* global exports:true, module:true, require:true, define:true, global:true */ + +(function (root, name, factory) { + /* jshint eqeqeq:false */ + 'use strict'; + + // Used to determine if values are of the language type `Object` + var objectTypes = { + 'function': true + , 'object': true + } + // Detect free variable `exports` + , freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports + // Detect free variable `module` + , freeModule = objectTypes["object"] && module && !module.nodeType && module + // Detect free variable `global`, from Node.js or Browserified code, and + // use it as `window` + , freeGlobal = freeExports && freeModule && typeof global == 'object' && global + // Detect the popular CommonJS extension `module.exports` + , moduleExports = freeModule && freeModule.exports === freeExports && freeExports; + + if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) { + root = freeGlobal; + } + + // Some AMD build optimizers, like r.js, check for specific condition + // patterns like the following: + if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { + // defined as an anonymous module. + define(['exports'], factory); + // In case the source has been processed and wrapped in a define module use + // the supplied `exports` object. + if (freeExports && moduleExports) factory(freeModule.exports); + } + // check for `exports` after `define` in case a build optimizer adds an + // `exports` object + else if (freeExports && freeModule) { + // in Node.js or RingoJS v0.8.0+ + if (moduleExports) factory(freeModule.exports); + // in Narwhal or RingoJS v0.7.0- + else factory(freeExports); + } + // in a browser or Rhino + else { + factory((root[name] = {})); + } +}(this, 'luaparse', function (exports) { + 'use strict'; + + exports.version = '0.2.1'; + + var input, options, length; + + // Options can be set either globally on the parser object through + // defaultOptions, or during the parse call. + var defaultOptions = exports.defaultOptions = { + // Explicitly tell the parser when the input ends. + wait: false + // Store comments as an array in the chunk object. + , comments: true + // Track identifier scopes by adding an isLocal attribute to each + // identifier-node. + , scope: false + // Store location information on each syntax node as + // `loc: { start: { line, column }, end: { line, column } }`. + , locations: false + // Store the start and end character locations on each syntax node as + // `range: [start, end]`. + , ranges: false + // A callback which will be invoked when a syntax node has been completed. + // The node which has been created will be passed as the only parameter. + , onCreateNode: null + // A callback which will be invoked when a new scope is created. + , onCreateScope: null + // A callback which will be invoked when the current scope is destroyed. + , onDestroyScope: null + }; + + // The available tokens expressed as enum flags so they can be checked with + // bitwise operations. + + var EOF = 1, StringLiteral = 2, Keyword = 4, Identifier = 8 + , NumericLiteral = 16, Punctuator = 32, BooleanLiteral = 64 + , NilLiteral = 128, VarargLiteral = 256; + + exports.tokenTypes = { EOF: EOF, StringLiteral: StringLiteral + , Keyword: Keyword, Identifier: Identifier, NumericLiteral: NumericLiteral + , Punctuator: Punctuator, BooleanLiteral: BooleanLiteral + , NilLiteral: NilLiteral, VarargLiteral: VarargLiteral + }; + + // As this parser is a bit different from luas own, the error messages + // will be different in some situations. + + var errors = exports.errors = { + unexpected: 'unexpected %1 \'%2\' near \'%3\'' + , expected: '\'%1\' expected near \'%2\'' + , expectedToken: '%1 expected near \'%2\'' + , unfinishedString: 'unfinished string near \'%1\'' + , malformedNumber: 'malformed number near \'%1\'' + , invalidVar: 'invalid left-hand side of assignment near \'%1\'' + }; + + // ### Abstract Syntax Tree + // + // The default AST structure is inspired by the Mozilla Parser API but can + // easily be customized by overriding these functions. + + var ast = exports.ast = { + labelStatement: function(label) { + return { + type: 'LabelStatement' + , label: label + }; + } + + , breakStatement: function() { + return { + type: 'BreakStatement' + }; + } + + , gotoStatement: function(label) { + return { + type: 'GotoStatement' + , label: label + }; + } + + , returnStatement: function(args) { + return { + type: 'ReturnStatement' + , 'arguments': args + }; + } + + , ifStatement: function(clauses) { + return { + type: 'IfStatement' + , clauses: clauses + }; + } + , ifClause: function(condition, body) { + return { + type: 'IfClause' + , condition: condition + , body: body + }; + } + , elseifClause: function(condition, body) { + return { + type: 'ElseifClause' + , condition: condition + , body: body + }; + } + , elseClause: function(body) { + return { + type: 'ElseClause' + , body: body + }; + } + + , whileStatement: function(condition, body) { + return { + type: 'WhileStatement' + , condition: condition + , body: body + }; + } + + , doStatement: function(body) { + return { + type: 'DoStatement' + , body: body + }; + } + + , repeatStatement: function(condition, body) { + return { + type: 'RepeatStatement' + , condition: condition + , body: body + }; + } + + , localStatement: function(variables, init) { + return { + type: 'LocalStatement' + , variables: variables + , init: init + }; + } + + , assignmentStatement: function(variables, init) { + return { + type: 'AssignmentStatement' + , variables: variables + , init: init + }; + } + + , callStatement: function(expression) { + return { + type: 'CallStatement' + , expression: expression + }; + } + + , functionStatement: function(identifier, parameters, isLocal, body) { + return { + type: 'FunctionDeclaration' + , identifier: identifier + , isLocal: isLocal + , parameters: parameters + , body: body + }; + } + + , forNumericStatement: function(variable, start, end, step, body) { + return { + type: 'ForNumericStatement' + , variable: variable + , start: start + , end: end + , step: step + , body: body + }; + } + + , forGenericStatement: function(variables, iterators, body) { + return { + type: 'ForGenericStatement' + , variables: variables + , iterators: iterators + , body: body + }; + } + + , chunk: function(body) { + return { + type: 'Chunk' + , body: body + }; + } + + , identifier: function(name) { + return { + type: 'Identifier' + , name: name + }; + } + + , literal: function(type, value, raw) { + type = (type === StringLiteral) ? 'StringLiteral' + : (type === NumericLiteral) ? 'NumericLiteral' + : (type === BooleanLiteral) ? 'BooleanLiteral' + : (type === NilLiteral) ? 'NilLiteral' + : 'VarargLiteral'; + + return { + type: type + , value: value + , raw: raw + }; + } + + , tableKey: function(key, value) { + return { + type: 'TableKey' + , key: key + , value: value + }; + } + , tableKeyString: function(key, value) { + return { + type: 'TableKeyString' + , key: key + , value: value + }; + } + , tableValue: function(value) { + return { + type: 'TableValue' + , value: value + }; + } + + + , tableConstructorExpression: function(fields) { + return { + type: 'TableConstructorExpression' + , fields: fields + }; + } + , binaryExpression: function(operator, left, right) { + var type = ('and' === operator || 'or' === operator) ? + 'LogicalExpression' : + 'BinaryExpression'; + + return { + type: type + , operator: operator + , left: left + , right: right + }; + } + , unaryExpression: function(operator, argument) { + return { + type: 'UnaryExpression' + , operator: operator + , argument: argument + }; + } + , memberExpression: function(base, indexer, identifier) { + return { + type: 'MemberExpression' + , indexer: indexer + , identifier: identifier + , base: base + }; + } + + , indexExpression: function(base, index) { + return { + type: 'IndexExpression' + , base: base + , index: index + }; + } + + , callExpression: function(base, args) { + return { + type: 'CallExpression' + , base: base + , 'arguments': args + }; + } + + , tableCallExpression: function(base, args) { + return { + type: 'TableCallExpression' + , base: base + , 'arguments': args + }; + } + + , stringCallExpression: function(base, argument) { + return { + type: 'StringCallExpression' + , base: base + , argument: argument + }; + } + + , comment: function(value, raw) { + return { + type: 'Comment' + , value: value + , raw: raw + }; + } + }; + + // Wrap up the node object. + + function finishNode(node) { + // Pop a `Marker` off the location-array and attach its location data. + if (trackLocations) { + var location = locations.pop(); + location.complete(); + if (options.locations) node.loc = location.loc; + if (options.ranges) node.range = location.range; + } + if (options.onCreateNode) options.onCreateNode(node); + return node; + } + + + // Helpers + // ------- + + var slice = Array.prototype.slice + , toString = Object.prototype.toString + , indexOf = function indexOf(array, element) { + for (var i = 0, length = array.length; i < length; i++) { + if (array[i] === element) return i; + } + return -1; + }; + + // Iterate through an array of objects and return the index of an object + // with a matching property. + + function indexOfObject(array, property, element) { + for (var i = 0, length = array.length; i < length; i++) { + if (array[i][property] === element) return i; + } + return -1; + } + + // A sprintf implementation using %index (beginning at 1) to input + // arguments in the format string. + // + // Example: + // + // // Unexpected function in token + // sprintf('Unexpected %2 in %1.', 'token', 'function'); + + function sprintf(format) { + var args = slice.call(arguments, 1); + format = format.replace(/%(\d)/g, function (match, index) { + return '' + args[index - 1] || ''; + }); + return format; + } + + // Returns a new object with the properties from all objectes passed as + // arguments. Last argument takes precedence. + // + // Example: + // + // this.options = extend(options, { output: false }); + + function extend() { + var args = slice.call(arguments) + , dest = {} + , src, prop; + + for (var i = 0, length = args.length; i < length; i++) { + src = args[i]; + for (prop in src) if (src.hasOwnProperty(prop)) { + dest[prop] = src[prop]; + } + } + return dest; + } + + // ### Error functions + + // #### Raise an exception. + // + // Raise an exception by passing a token, a string format and its paramters. + // + // The passed tokens location will automatically be added to the error + // message if it exists, if not it will default to the lexers current + // position. + // + // Example: + // + // // [1:0] expected [ near ( + // raise(token, "expected %1 near %2", '[', token.value); + + function raise(token) { + var message = sprintf.apply(null, slice.call(arguments, 1)) + , error, col; + + if ('undefined' !== typeof token.line) { + col = token.range[0] - token.lineStart; + error = new SyntaxError(sprintf('[%1:%2] %3', token.line, col, message)); + error.line = token.line; + error.index = token.range[0]; + error.column = col; + } else { + col = index - lineStart + 1; + error = new SyntaxError(sprintf('[%1:%2] %3', line, col, message)); + error.index = index; + error.line = line; + error.column = col; + } + throw error; + } + + // #### Raise an unexpected token error. + // + // Example: + // + // // expected near '0' + // raiseUnexpectedToken('', token); + + function raiseUnexpectedToken(type, token) { + raise(token, errors.expectedToken, type, token.value); + } + + // #### Raise a general unexpected error + // + // Usage should pass either a token object or a symbol string which was + // expected. We can also specify a nearby token such as , this will + // default to the currently active token. + // + // Example: + // + // // Unexpected symbol 'end' near '' + // unexpected(token); + // + // If there's no token in the buffer it means we have reached . + + function unexpected(found, near) { + if ('undefined' === typeof near) near = lookahead.value; + if ('undefined' !== typeof found.type) { + var type; + switch (found.type) { + case StringLiteral: type = 'string'; break; + case Keyword: type = 'keyword'; break; + case Identifier: type = 'identifier'; break; + case NumericLiteral: type = 'number'; break; + case Punctuator: type = 'symbol'; break; + case BooleanLiteral: type = 'boolean'; break; + case NilLiteral: + return raise(found, errors.unexpected, 'symbol', 'nil', near); + } + return raise(found, errors.unexpected, type, found.value, near); + } + return raise(found, errors.unexpected, 'symbol', found, near); + } + + // Lexer + // ----- + // + // The lexer, or the tokenizer reads the input string character by character + // and derives a token left-right. To be as efficient as possible the lexer + // prioritizes the common cases such as identifiers. It also works with + // character codes instead of characters as string comparisons was the + // biggest bottleneck of the parser. + // + // If `options.comments` is enabled, all comments encountered will be stored + // in an array which later will be appended to the chunk object. If disabled, + // they will simply be disregarded. + // + // When the lexer has derived a valid token, it will be returned as an object + // containing its value and as well as its position in the input string (this + // is always enabled to provide proper debug messages). + // + // `lex()` starts lexing and returns the following token in the stream. + + var index + , token + , previousToken + , lookahead + , comments + , tokenStart + , line + , lineStart; + + exports.lex = lex; + + function lex() { + skipWhiteSpace(); + + // Skip comments beginning with -- + while (45 === input.charCodeAt(index) && + 45 === input.charCodeAt(index + 1)) { + scanComment(); + skipWhiteSpace(); + } + if (index >= length) return { + type : EOF + , value: '' + , line: line + , lineStart: lineStart + , range: [index, index] + }; + + var charCode = input.charCodeAt(index) + , next = input.charCodeAt(index + 1); + + // Memorize the range index where the token begins. + tokenStart = index; + if (isIdentifierStart(charCode)) return scanIdentifierOrKeyword(); + + switch (charCode) { + case 39: case 34: // '" + return scanStringLiteral(); + + // 0-9 + case 48: case 49: case 50: case 51: case 52: case 53: + case 54: case 55: case 56: case 57: + return scanNumericLiteral(); + + case 46: // . + // If the dot is followed by a digit it's a float. + if (isDecDigit(next)) return scanNumericLiteral(); + if (46 === next) { + if (46 === input.charCodeAt(index + 2)) return scanVarargLiteral(); + return scanPunctuator('..'); + } + return scanPunctuator('.'); + + case 61: // = + if (61 === next) return scanPunctuator('=='); + return scanPunctuator('='); + + case 62: // > + if (61 === next) return scanPunctuator('>='); + if (62 === next) return scanPunctuator('>>'); + return scanPunctuator('>'); + + case 60: // < + if (60 === next) return scanPunctuator('<<'); + if (61 === next) return scanPunctuator('<='); + return scanPunctuator('<'); + + case 126: // ~ + if (61 === next) return scanPunctuator('~='); + return scanPunctuator('~'); + + case 58: // : + if (58 === next) return scanPunctuator('::'); + return scanPunctuator(':'); + + case 91: // [ + // Check for a multiline string, they begin with [= or [[ + if (91 === next || 61 === next) return scanLongStringLiteral(); + return scanPunctuator('['); + + case 47: // / + // Check for integer division op (//) + if (47 === next) return scanPunctuator('//'); + return scanPunctuator('/'); + + // * ^ % , { } ] ( ) ; & # - + | + case 42: case 94: case 37: case 44: case 123: case 124: case 125: + case 93: case 40: case 41: case 59: case 38: case 35: case 45: case 43: + return scanPunctuator(input.charAt(index)); + } + + return unexpected(input.charAt(index)); + } + + // Whitespace has no semantic meaning in lua so simply skip ahead while + // tracking the encounted newlines. Any kind of eol sequence is counted as a + // single line. + + function consumeEOL() { + var charCode = input.charCodeAt(index) + , peekCharCode = input.charCodeAt(index + 1); + + if (isLineTerminator(charCode)) { + // Count \n\r and \r\n as one newline. + if (10 === charCode && 13 === peekCharCode) index++; + if (13 === charCode && 10 === peekCharCode) index++; + line++; + lineStart = ++index; + + return true; + } + return false; + } + + function skipWhiteSpace() { + while (index < length) { + var charCode = input.charCodeAt(index); + if (isWhiteSpace(charCode)) { + index++; + } else if (!consumeEOL()) { + break; + } + } + } + + // Identifiers, keywords, booleans and nil all look the same syntax wise. We + // simply go through them one by one and defaulting to an identifier if no + // previous case matched. + + function scanIdentifierOrKeyword() { + var value, type; + + // Slicing the input string is prefered before string concatenation in a + // loop for performance reasons. + while (isIdentifierPart(input.charCodeAt(++index))); + value = input.slice(tokenStart, index); + + // Decide on the token type and possibly cast the value. + if (isKeyword(value)) { + type = Keyword; + } else if ('true' === value || 'false' === value) { + type = BooleanLiteral; + value = ('true' === value); + } else if ('nil' === value) { + type = NilLiteral; + value = null; + } else { + type = Identifier; + } + + return { + type: type + , value: value + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Once a punctuator reaches this function it should already have been + // validated so we simply return it as a token. + + function scanPunctuator(value) { + index += value.length; + return { + type: Punctuator + , value: value + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // A vararg literal consists of three dots. + + function scanVarargLiteral() { + index += 3; + return { + type: VarargLiteral + , value: '...' + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Find the string literal by matching the delimiter marks used. + + function scanStringLiteral() { + var delimiter = input.charCodeAt(index++) + , stringStart = index + , string = '' + , charCode; + + while (index < length) { + charCode = input.charCodeAt(index++); + if (delimiter === charCode) break; + if (92 === charCode) { // \ + string += input.slice(stringStart, index - 1) + readEscapeSequence(); + stringStart = index; + } + // EOF or `\n` terminates a string literal. If we haven't found the + // ending delimiter by now, raise an exception. + else if (index >= length || isLineTerminator(charCode)) { + string += input.slice(stringStart, index - 1); + raise({}, errors.unfinishedString, string + String.fromCharCode(charCode)); + } + } + string += input.slice(stringStart, index - 1); + + return { + type: StringLiteral + , value: string + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Expect a multiline string literal and return it as a regular string + // literal, if it doesn't validate into a valid multiline string, throw an + // exception. + + function scanLongStringLiteral() { + var string = readLongString(); + // Fail if it's not a multiline literal. + if (false === string) raise(token, errors.expected, '[', token.value); + + return { + type: StringLiteral + , value: string + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Numeric literals will be returned as floating-point numbers instead of + // strings. The raw value should be retrieved from slicing the input string + // later on in the process. + // + // If a hexadecimal number is encountered, it will be converted. + + function scanNumericLiteral() { + var character = input.charAt(index) + , next = input.charAt(index + 1); + + var value = ('0' === character && 'xX'.indexOf(next || null) >= 0) ? + readHexLiteral() : readDecLiteral(); + + return { + type: NumericLiteral + , value: value + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Lua hexadecimals have an optional fraction part and an optional binary + // exoponent part. These are not included in JavaScript so we will compute + // all three parts separately and then sum them up at the end of the function + // with the following algorithm. + // + // Digit := toDec(digit) + // Fraction := toDec(fraction) / 16 ^ fractionCount + // BinaryExp := 2 ^ binaryExp + // Number := ( Digit + Fraction ) * BinaryExp + + function readHexLiteral() { + var fraction = 0 // defaults to 0 as it gets summed + , binaryExponent = 1 // defaults to 1 as it gets multiplied + , binarySign = 1 // positive + , digit, fractionStart, exponentStart, digitStart; + + digitStart = index += 2; // Skip 0x part + + // A minimum of one hex digit is required. + if (!isHexDigit(input.charCodeAt(index))) + raise({}, errors.malformedNumber, input.slice(tokenStart, index)); + + while (isHexDigit(input.charCodeAt(index))) index++; + // Convert the hexadecimal digit to base 10. + digit = parseInt(input.slice(digitStart, index), 16); + + // Fraction part i optional. + if ('.' === input.charAt(index)) { + fractionStart = ++index; + + while (isHexDigit(input.charCodeAt(index))) index++; + fraction = input.slice(fractionStart, index); + + // Empty fraction parts should default to 0, others should be converted + // 0.x form so we can use summation at the end. + fraction = (fractionStart === index) ? 0 + : parseInt(fraction, 16) / Math.pow(16, index - fractionStart); + } + + // Binary exponents are optional + if ('pP'.indexOf(input.charAt(index) || null) >= 0) { + index++; + + // Sign part is optional and defaults to 1 (positive). + if ('+-'.indexOf(input.charAt(index) || null) >= 0) + binarySign = ('+' === input.charAt(index++)) ? 1 : -1; + + exponentStart = index; + + // The binary exponent sign requires a decimal digit. + if (!isDecDigit(input.charCodeAt(index))) + raise({}, errors.malformedNumber, input.slice(tokenStart, index)); + + while (isDecDigit(input.charCodeAt(index))) index++; + binaryExponent = input.slice(exponentStart, index); + + // Calculate the binary exponent of the number. + binaryExponent = Math.pow(2, binaryExponent * binarySign); + } + + return (digit + fraction) * binaryExponent; + } + + // Decimal numbers are exactly the same in Lua and in JavaScript, because of + // this we check where the token ends and then parse it with native + // functions. + + function readDecLiteral() { + while (isDecDigit(input.charCodeAt(index))) index++; + // Fraction part is optional + if ('.' === input.charAt(index)) { + index++; + // Fraction part defaults to 0 + while (isDecDigit(input.charCodeAt(index))) index++; + } + // Exponent part is optional. + if ('eE'.indexOf(input.charAt(index) || null) >= 0) { + index++; + // Sign part is optional. + if ('+-'.indexOf(input.charAt(index) || null) >= 0) index++; + // An exponent is required to contain at least one decimal digit. + if (!isDecDigit(input.charCodeAt(index))) + raise({}, errors.malformedNumber, input.slice(tokenStart, index)); + + while (isDecDigit(input.charCodeAt(index))) index++; + } + + return parseFloat(input.slice(tokenStart, index)); + } + + + // Translate escape sequences to the actual characters. + + function readEscapeSequence() { + var sequenceStart = index; + switch (input.charAt(index)) { + // Lua allow the following escape sequences. + // We don't escape the bell sequence. + case 'n': index++; return '\n'; + case 'r': index++; return '\r'; + case 't': index++; return '\t'; + case 'v': index++; return '\x0B'; + case 'b': index++; return '\b'; + case 'f': index++; return '\f'; + // Skips the following span of white-space. + case 'z': index++; skipWhiteSpace(); return ''; + // Byte representation should for now be returned as is. + case 'x': + // \xXX, where XX is a sequence of exactly two hexadecimal digits + if (isHexDigit(input.charCodeAt(index + 1)) && + isHexDigit(input.charCodeAt(index + 2))) { + index += 3; + // Return it as is, without translating the byte. + return '\\' + input.slice(sequenceStart, index); + } + return '\\' + input.charAt(index++); + default: + // \ddd, where ddd is a sequence of up to three decimal digits. + if (isDecDigit(input.charCodeAt(index))) { + while (isDecDigit(input.charCodeAt(++index))); + return '\\' + input.slice(sequenceStart, index); + } + // Simply return the \ as is, it's not escaping any sequence. + return input.charAt(index++); + } + } + + // Comments begin with -- after which it will be decided if they are + // multiline comments or not. + // + // The multiline functionality works the exact same way as with string + // literals so we reuse the functionality. + + function scanComment() { + tokenStart = index; + index += 2; // -- + + var character = input.charAt(index) + , content = '' + , isLong = false + , commentStart = index + , lineStartComment = lineStart + , lineComment = line; + + if ('[' === character) { + content = readLongString(); + // This wasn't a multiline comment after all. + if (false === content) content = character; + else isLong = true; + } + // Scan until next line as long as it's not a multiline comment. + if (!isLong) { + while (index < length) { + if (isLineTerminator(input.charCodeAt(index))) break; + index++; + } + if (options.comments) content = input.slice(commentStart, index); + } + + if (options.comments) { + var node = ast.comment(content, input.slice(tokenStart, index)); + + // `Marker`s depend on tokens available in the parser and as comments are + // intercepted in the lexer all location data is set manually. + if (options.locations) { + node.loc = { + start: { line: lineComment, column: tokenStart - lineStartComment } + , end: { line: line, column: index - lineStart } + }; + } + if (options.ranges) { + node.range = [tokenStart, index]; + } + if (options.onCreateNode) options.onCreateNode(node); + comments.push(node); + } + } + + // Read a multiline string by calculating the depth of `=` characters and + // then appending until an equal depth is found. + + function readLongString() { + var level = 0 + , content = '' + , terminator = false + , character, stringStart; + + index++; // [ + + // Calculate the depth of the comment. + while ('=' === input.charAt(index + level)) level++; + // Exit, this is not a long string afterall. + if ('[' !== input.charAt(index + level)) return false; + + index += level + 1; + + // If the first character is a newline, ignore it and begin on next line. + if (isLineTerminator(input.charCodeAt(index))) consumeEOL(); + + stringStart = index; + while (index < length) { + // To keep track of line numbers run the `consumeEOL()` which increments + // its counter. + if (isLineTerminator(input.charCodeAt(index))) consumeEOL(); + + character = input.charAt(index++); + + // Once the delimiter is found, iterate through the depth count and see + // if it matches. + if (']' === character) { + terminator = true; + for (var i = 0; i < level; i++) { + if ('=' !== input.charAt(index + i)) terminator = false; + } + if (']' !== input.charAt(index + level)) terminator = false; + } + + // We reached the end of the multiline string. Get out now. + if (terminator) break; + } + content += input.slice(stringStart, index - 1); + index += level + 1; + + return content; + } + + // ## Lex functions and helpers. + + // Read the next token. + // + // This is actually done by setting the current token to the lookahead and + // reading in the new lookahead token. + + function next() { + previousToken = token; + token = lookahead; + lookahead = lex(); + } + + // Consume a token if its value matches. Once consumed or not, return the + // success of the operation. + + function consume(value) { + if (value === token.value) { + next(); + return true; + } + return false; + } + + // Expect the next token value to match. If not, throw an exception. + + function expect(value) { + if (value === token.value) next(); + else raise(token, errors.expected, value, token.value); + } + + // ### Validation functions + + function isWhiteSpace(charCode) { + return 9 === charCode || 32 === charCode || 0xB === charCode || 0xC === charCode; + } + + function isLineTerminator(charCode) { + return 10 === charCode || 13 === charCode; + } + + function isDecDigit(charCode) { + return charCode >= 48 && charCode <= 57; + } + + function isHexDigit(charCode) { + return (charCode >= 48 && charCode <= 57) || (charCode >= 97 && charCode <= 102) || (charCode >= 65 && charCode <= 70); + } + + // From [Lua 5.2](http://www.lua.org/manual/5.2/manual.html#8.1) onwards + // identifiers cannot use locale-dependet letters. + + function isIdentifierStart(charCode) { + return (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122) || 95 === charCode; + } + + function isIdentifierPart(charCode) { + return (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122) || 95 === charCode || (charCode >= 48 && charCode <= 57); + } + + // [3.1 Lexical Conventions](http://www.lua.org/manual/5.2/manual.html#3.1) + // + // `true`, `false` and `nil` will not be considered keywords, but literals. + + function isKeyword(id) { + switch (id.length) { + case 2: + return 'do' === id || 'if' === id || 'in' === id || 'or' === id; + case 3: + return 'and' === id || 'end' === id || 'for' === id || 'not' === id; + case 4: + return 'else' === id || 'goto' === id || 'then' === id; + case 5: + return 'break' === id || 'local' === id || 'until' === id || 'while' === id; + case 6: + return 'elseif' === id || 'repeat' === id || 'return' === id; + case 8: + return 'function' === id; + } + return false; + } + + function isUnary(token) { + if (Punctuator === token.type) return '#-~'.indexOf(token.value) >= 0; + if (Keyword === token.type) return 'not' === token.value; + return false; + } + + // @TODO this needs to be rethought. + function isCallExpression(expression) { + switch (expression.type) { + case 'CallExpression': + case 'TableCallExpression': + case 'StringCallExpression': + return true; + } + return false; + } + + // Check if the token syntactically closes a block. + + function isBlockFollow(token) { + if (EOF === token.type) return true; + if (Keyword !== token.type) return false; + switch (token.value) { + case 'else': case 'elseif': + case 'end': case 'until': + return true; + default: + return false; + } + } + + // Scope + // ----- + + // Store each block scope as a an array of identifier names. Each scope is + // stored in an FILO-array. + var scopes + // The current scope index + , scopeDepth + // A list of all global identifier nodes. + , globals; + + // Create a new scope inheriting all declarations from the previous scope. + function createScope() { + var scope = Array.apply(null, scopes[scopeDepth++]); + scopes.push(scope); + if (options.onCreateScope) options.onCreateScope(); + } + + // Exit and remove the current scope. + function destroyScope() { + var scope = scopes.pop(); + scopeDepth--; + if (options.onDestroyScope) options.onDestroyScope(); + } + + // Add identifier name to the current scope if it doesnt already exist. + function scopeIdentifierName(name) { + if (-1 !== indexOf(scopes[scopeDepth], name)) return; + scopes[scopeDepth].push(name); + } + + // Add identifier to the current scope + function scopeIdentifier(node) { + scopeIdentifierName(node.name); + attachScope(node, true); + } + + // Attach scope information to node. If the node is global, store it in the + // globals array so we can return the information to the user. + function attachScope(node, isLocal) { + if (!isLocal && -1 === indexOfObject(globals, 'name', node.name)) + globals.push(node); + + node.isLocal = isLocal; + } + + // Is the identifier name available in this scope. + function scopeHasName(name) { + return (-1 !== indexOf(scopes[scopeDepth], name)); + } + + // Location tracking + // ----------------- + // + // Locations are stored in FILO-array as a `Marker` object consisting of both + // `loc` and `range` data. Once a `Marker` is popped off the list an end + // location is added and the data is attached to a syntax node. + + var locations = [] + , trackLocations; + + function createLocationMarker() { + return new Marker(token); + } + + function Marker(token) { + if (options.locations) { + this.loc = { + start: { + line: token.line + , column: token.range[0] - token.lineStart + } + , end: { + line: 0 + , column: 0 + } + }; + } + if (options.ranges) this.range = [token.range[0], 0]; + } + + // Complete the location data stored in the `Marker` by adding the location + // of the *previous token* as an end location. + Marker.prototype.complete = function() { + if (options.locations) { + this.loc.end.line = previousToken.line; + this.loc.end.column = previousToken.range[1] - previousToken.lineStart; + } + if (options.ranges) { + this.range[1] = previousToken.range[1]; + } + }; + + // Create a new `Marker` and add it to the FILO-array. + function markLocation() { + if (trackLocations) locations.push(createLocationMarker()); + } + + // Push an arbitrary `Marker` object onto the FILO-array. + function pushLocation(marker) { + if (trackLocations) locations.push(marker); + } + + // Parse functions + // --------------- + + // Chunk is the main program object. Syntactically it's the same as a block. + // + // chunk ::= block + + function parseChunk() { + next(); + markLocation(); + if (options.scope) createScope(); + var body = parseBlock(); + if (options.scope) destroyScope(); + if (EOF !== token.type) unexpected(token); + // If the body is empty no previousToken exists when finishNode runs. + if (trackLocations && !body.length) previousToken = token; + return finishNode(ast.chunk(body)); + } + + // A block contains a list of statements with an optional return statement + // as its last statement. + // + // block ::= {stat} [retstat] + + function parseBlock(terminator) { + var block = [] + , statement; + + while (!isBlockFollow(token)) { + // Return has to be the last statement in a block. + if ('return' === token.value) { + block.push(parseStatement()); + break; + } + statement = parseStatement(); + // Statements are only added if they are returned, this allows us to + // ignore some statements, such as EmptyStatement. + if (statement) block.push(statement); + } + + // Doesn't really need an ast node + return block; + } + + // There are two types of statements, simple and compound. + // + // statement ::= break | goto | do | while | repeat | return + // | if | for | function | local | label | assignment + // | functioncall | ';' + + function parseStatement() { + markLocation(); + if (Keyword === token.type) { + switch (token.value) { + case 'local': next(); return parseLocalStatement(); + case 'if': next(); return parseIfStatement(); + case 'return': next(); return parseReturnStatement(); + case 'function': next(); + var name = parseFunctionName(); + return parseFunctionDeclaration(name); + case 'while': next(); return parseWhileStatement(); + case 'for': next(); return parseForStatement(); + case 'repeat': next(); return parseRepeatStatement(); + case 'break': next(); return parseBreakStatement(); + case 'do': next(); return parseDoStatement(); + case 'goto': next(); return parseGotoStatement(); + } + } + + if (Punctuator === token.type) { + if (consume('::')) return parseLabelStatement(); + } + // Assignments memorizes the location and pushes it manually for wrapper + // nodes. Additionally empty `;` statements should not mark a location. + if (trackLocations) locations.pop(); + + // When a `;` is encounted, simply eat it without storing it. + if (consume(';')) return; + + return parseAssignmentOrCallStatement(); + } + + // ## Statements + + // label ::= '::' Name '::' + + function parseLabelStatement() { + var name = token.value + , label = parseIdentifier(); + + if (options.scope) { + scopeIdentifierName('::' + name + '::'); + attachScope(label, true); + } + + expect('::'); + return finishNode(ast.labelStatement(label)); + } + + // break ::= 'break' + + function parseBreakStatement() { + return finishNode(ast.breakStatement()); + } + + // goto ::= 'goto' Name + + function parseGotoStatement() { + var name = token.value + , label = parseIdentifier(); + + return finishNode(ast.gotoStatement(label)); + } + + // do ::= 'do' block 'end' + + function parseDoStatement() { + if (options.scope) createScope(); + var body = parseBlock(); + if (options.scope) destroyScope(); + expect('end'); + return finishNode(ast.doStatement(body)); + } + + // while ::= 'while' exp 'do' block 'end' + + function parseWhileStatement() { + var condition = parseExpectedExpression(); + expect('do'); + if (options.scope) createScope(); + var body = parseBlock(); + if (options.scope) destroyScope(); + expect('end'); + return finishNode(ast.whileStatement(condition, body)); + } + + // repeat ::= 'repeat' block 'until' exp + + function parseRepeatStatement() { + if (options.scope) createScope(); + var body = parseBlock(); + expect('until'); + var condition = parseExpectedExpression(); + if (options.scope) destroyScope(); + return finishNode(ast.repeatStatement(condition, body)); + } + + // retstat ::= 'return' [exp {',' exp}] [';'] + + function parseReturnStatement() { + var expressions = []; + + if ('end' !== token.value) { + var expression = parseExpression(); + if (null != expression) expressions.push(expression); + while (consume(',')) { + expression = parseExpectedExpression(); + expressions.push(expression); + } + consume(';'); // grammar tells us ; is optional here. + } + return finishNode(ast.returnStatement(expressions)); + } + + // if ::= 'if' exp 'then' block {elif} ['else' block] 'end' + // elif ::= 'elseif' exp 'then' block + + function parseIfStatement() { + var clauses = [] + , condition + , body + , marker; + + // IfClauses begin at the same location as the parent IfStatement. + // It ends at the start of `end`, `else`, or `elseif`. + if (trackLocations) { + marker = locations[locations.length - 1]; + locations.push(marker); + } + condition = parseExpectedExpression(); + expect('then'); + if (options.scope) createScope(); + body = parseBlock(); + if (options.scope) destroyScope(); + clauses.push(finishNode(ast.ifClause(condition, body))); + + if (trackLocations) marker = createLocationMarker(); + while (consume('elseif')) { + pushLocation(marker); + condition = parseExpectedExpression(); + expect('then'); + if (options.scope) createScope(); + body = parseBlock(); + if (options.scope) destroyScope(); + clauses.push(finishNode(ast.elseifClause(condition, body))); + if (trackLocations) marker = createLocationMarker(); + } + + if (consume('else')) { + // Include the `else` in the location of ElseClause. + if (trackLocations) { + marker = new Marker(previousToken); + locations.push(marker); + } + if (options.scope) createScope(); + body = parseBlock(); + if (options.scope) destroyScope(); + clauses.push(finishNode(ast.elseClause(body))); + } + + expect('end'); + return finishNode(ast.ifStatement(clauses)); + } + + // There are two types of for statements, generic and numeric. + // + // for ::= Name '=' exp ',' exp [',' exp] 'do' block 'end' + // for ::= namelist 'in' explist 'do' block 'end' + // namelist ::= Name {',' Name} + // explist ::= exp {',' exp} + + function parseForStatement() { + var variable = parseIdentifier() + , body; + + // The start-identifier is local. + + if (options.scope) { + createScope(); + scopeIdentifier(variable); + } + + // If the first expression is followed by a `=` punctuator, this is a + // Numeric For Statement. + if (consume('=')) { + // Start expression + var start = parseExpectedExpression(); + expect(','); + // End expression + var end = parseExpectedExpression(); + // Optional step expression + var step = consume(',') ? parseExpectedExpression() : null; + + expect('do'); + body = parseBlock(); + expect('end'); + if (options.scope) destroyScope(); + + return finishNode(ast.forNumericStatement(variable, start, end, step, body)); + } + // If not, it's a Generic For Statement + else { + // The namelist can contain one or more identifiers. + var variables = [variable]; + while (consume(',')) { + variable = parseIdentifier(); + // Each variable in the namelist is locally scoped. + if (options.scope) scopeIdentifier(variable); + variables.push(variable); + } + expect('in'); + var iterators = []; + + // One or more expressions in the explist. + do { + var expression = parseExpectedExpression(); + iterators.push(expression); + } while (consume(',')); + + expect('do'); + body = parseBlock(); + expect('end'); + if (options.scope) destroyScope(); + + return finishNode(ast.forGenericStatement(variables, iterators, body)); + } + } + + // Local statements can either be variable assignments or function + // definitions. If a function definition is found, it will be delegated to + // `parseFunctionDeclaration()` with the isLocal flag. + // + // This AST structure might change into a local assignment with a function + // child. + // + // local ::= 'local' 'function' Name funcdecl + // | 'local' Name {',' Name} ['=' exp {',' exp}] + + function parseLocalStatement() { + var name; + + if (Identifier === token.type) { + var variables = [] + , init = []; + + do { + name = parseIdentifier(); + + variables.push(name); + } while (consume(',')); + + if (consume('=')) { + do { + var expression = parseExpectedExpression(); + init.push(expression); + } while (consume(',')); + } + + // Declarations doesn't exist before the statement has been evaluated. + // Therefore assignments can't use their declarator. And the identifiers + // shouldn't be added to the scope until the statement is complete. + if (options.scope) { + for (var i = 0, l = variables.length; i < l; i++) { + scopeIdentifier(variables[i]); + } + } + + return finishNode(ast.localStatement(variables, init)); + } + if (consume('function')) { + name = parseIdentifier(); + + if (options.scope) { + scopeIdentifier(name); + createScope(); + } + + // MemberExpressions are not allowed in local function statements. + return parseFunctionDeclaration(name, true); + } else { + raiseUnexpectedToken('', token); + } + } + + function validateVar(node) { + // @TODO we need something not dependent on the exact AST used. see also isCallExpression() + if (node.inParens || (['Identifier', 'MemberExpression', 'IndexExpression'].indexOf(node.type) === -1)) { + raise(token, errors.invalidVar, token.value); + } + } + + // assignment ::= varlist '=' explist + // var ::= Name | prefixexp '[' exp ']' | prefixexp '.' Name + // varlist ::= var {',' var} + // explist ::= exp {',' exp} + // + // call ::= callexp + // callexp ::= prefixexp args | prefixexp ':' Name args + + function parseAssignmentOrCallStatement() { + // Keep a reference to the previous token for better error messages in case + // of invalid statement + var previous = token + , expression, marker; + + if (trackLocations) marker = createLocationMarker(); + expression = parsePrefixExpression(); + + if (null == expression) return unexpected(token); + if (',='.indexOf(token.value) >= 0) { + var variables = [expression] + , init = [] + , exp; + + validateVar(expression); + while (consume(',')) { + exp = parsePrefixExpression(); + if (null == exp) raiseUnexpectedToken('', token); + validateVar(exp); + variables.push(exp); + } + expect('='); + do { + exp = parseExpectedExpression(); + init.push(exp); + } while (consume(',')); + + pushLocation(marker); + return finishNode(ast.assignmentStatement(variables, init)); + } + if (isCallExpression(expression)) { + pushLocation(marker); + return finishNode(ast.callStatement(expression)); + } + // The prefix expression was neither part of an assignment or a + // callstatement, however as it was valid it's been consumed, so raise + // the exception on the previous token to provide a helpful message. + return unexpected(previous); + } + + + + // ### Non-statements + + // Identifier ::= Name + + function parseIdentifier() { + markLocation(); + var identifier = token.value; + if (Identifier !== token.type) raiseUnexpectedToken('', token); + next(); + return finishNode(ast.identifier(identifier)); + } + + // Parse the functions parameters and body block. The name should already + // have been parsed and passed to this declaration function. By separating + // this we allow for anonymous functions in expressions. + // + // For local functions there's a boolean parameter which needs to be set + // when parsing the declaration. + // + // funcdecl ::= '(' [parlist] ')' block 'end' + // parlist ::= Name {',' Name} | [',' '...'] | '...' + + function parseFunctionDeclaration(name, isLocal) { + var parameters = []; + expect('('); + + // The declaration has arguments + if (!consume(')')) { + // Arguments are a comma separated list of identifiers, optionally ending + // with a vararg. + while (true) { + if (Identifier === token.type) { + var parameter = parseIdentifier(); + // Function parameters are local. + if (options.scope) scopeIdentifier(parameter); + + parameters.push(parameter); + + if (consume(',')) continue; + else if (consume(')')) break; + } + // No arguments are allowed after a vararg. + else if (VarargLiteral === token.type) { + parameters.push(parsePrimaryExpression()); + expect(')'); + break; + } else { + raiseUnexpectedToken(' or \'...\'', token); + } + } + } + + var body = parseBlock(); + expect('end'); + if (options.scope) destroyScope(); + + isLocal = isLocal || false; + return finishNode(ast.functionStatement(name, parameters, isLocal, body)); + } + + // Parse the function name as identifiers and member expressions. + // + // Name {'.' Name} [':' Name] + + function parseFunctionName() { + var base, name, marker; + + if (trackLocations) marker = createLocationMarker(); + base = parseIdentifier(); + + if (options.scope) { + attachScope(base, scopeHasName(base.name)); + createScope(); + } + + while (consume('.')) { + pushLocation(marker); + name = parseIdentifier(); + base = finishNode(ast.memberExpression(base, '.', name)); + } + + if (consume(':')) { + pushLocation(marker); + name = parseIdentifier(); + base = finishNode(ast.memberExpression(base, ':', name)); + if (options.scope) scopeIdentifierName('self'); + } + + return base; + } + + // tableconstructor ::= '{' [fieldlist] '}' + // fieldlist ::= field {fieldsep field} fieldsep + // field ::= '[' exp ']' '=' exp | Name = 'exp' | exp + // + // fieldsep ::= ',' | ';' + + function parseTableConstructor() { + var fields = [] + , key, value; + + while (true) { + markLocation(); + if (Punctuator === token.type && consume('[')) { + key = parseExpectedExpression(); + expect(']'); + expect('='); + value = parseExpectedExpression(); + fields.push(finishNode(ast.tableKey(key, value))); + } else if (Identifier === token.type) { + if ('=' === lookahead.value) { + key = parseIdentifier(); + next(); + value = parseExpectedExpression(); + fields.push(finishNode(ast.tableKeyString(key, value))); + } else { + value = parseExpectedExpression(); + fields.push(finishNode(ast.tableValue(value))); + } + } else { + if (null == (value = parseExpression())) { + locations.pop(); + break; + } + fields.push(finishNode(ast.tableValue(value))); + } + if (',;'.indexOf(token.value) >= 0) { + next(); + continue; + } + break; + } + expect('}'); + return finishNode(ast.tableConstructorExpression(fields)); + } + + // Expression parser + // ----------------- + // + // Expressions are evaluated and always return a value. If nothing is + // matched null will be returned. + // + // exp ::= (unop exp | primary | prefixexp ) { binop exp } + // + // primary ::= nil | false | true | Number | String | '...' + // | functiondef | tableconstructor + // + // prefixexp ::= (Name | '(' exp ')' ) { '[' exp ']' + // | '.' Name | ':' Name args | args } + // + + function parseExpression() { + var expression = parseSubExpression(0); + return expression; + } + + // Parse an expression expecting it to be valid. + + function parseExpectedExpression() { + var expression = parseExpression(); + if (null == expression) raiseUnexpectedToken('', token); + else return expression; + } + + + // Return the precedence priority of the operator. + // + // As unary `-` can't be distinguished from binary `-`, unary precedence + // isn't described in this table but in `parseSubExpression()` itself. + // + // As this function gets hit on every expression it's been optimized due to + // the expensive CompareICStub which took ~8% of the parse time. + + function binaryPrecedence(operator) { + var charCode = operator.charCodeAt(0) + , length = operator.length; + + if (1 === length) { + switch (charCode) { + case 94: return 12; // ^ + case 42: case 47: case 37: return 10; // * / % + case 43: case 45: return 9; // + - + case 38: return 6; // & + case 126: return 5; // ~ + case 124: return 4; // | + case 60: case 62: return 3; // < > + } + } else if (2 === length) { + switch (charCode) { + case 47: return 10; // // + case 46: return 8; // .. + case 60: case 62: + if('<<' === operator || '>>' === operator) return 7; // << >> + return 3; // <= >= + case 61: case 126: return 3; // == ~= + case 111: return 1; // or + } + } else if (97 === charCode && 'and' === operator) return 2; + return 0; + } + + // Implement an operator-precedence parser to handle binary operator + // precedence. + // + // We use this algorithm because it's compact, it's fast and Lua core uses + // the same so we can be sure our expressions are parsed in the same manner + // without excessive amounts of tests. + // + // exp ::= (unop exp | primary | prefixexp ) { binop exp } + + function parseSubExpression(minPrecedence) { + var operator = token.value + // The left-hand side in binary operations. + , expression, marker; + + if (trackLocations) marker = createLocationMarker(); + + // UnaryExpression + if (isUnary(token)) { + markLocation(); + next(); + var argument = parseSubExpression(10); + if (argument == null) raiseUnexpectedToken('', token); + expression = finishNode(ast.unaryExpression(operator, argument)); + } + if (null == expression) { + // PrimaryExpression + expression = parsePrimaryExpression(); + + // PrefixExpression + if (null == expression) { + expression = parsePrefixExpression(); + } + } + // This is not a valid left hand expression. + if (null == expression) return null; + + var precedence; + while (true) { + operator = token.value; + + precedence = (Punctuator === token.type || Keyword === token.type) ? + binaryPrecedence(operator) : 0; + + if (precedence === 0 || precedence <= minPrecedence) break; + // Right-hand precedence operators + if ('^' === operator || '..' === operator) precedence--; + next(); + var right = parseSubExpression(precedence); + if (null == right) raiseUnexpectedToken('', token); + // Push in the marker created before the loop to wrap its entirety. + if (trackLocations) locations.push(marker); + expression = finishNode(ast.binaryExpression(operator, expression, right)); + + } + return expression; + } + + // prefixexp ::= prefix {suffix} + // prefix ::= Name | '(' exp ')' + // suffix ::= '[' exp ']' | '.' Name | ':' Name args | args + // + // args ::= '(' [explist] ')' | tableconstructor | String + + function parsePrefixExpression() { + var base, name, marker; + + if (trackLocations) marker = createLocationMarker(); + + // The prefix + if (Identifier === token.type) { + name = token.value; + base = parseIdentifier(); + // Set the parent scope. + if (options.scope) attachScope(base, scopeHasName(name)); + } else if (consume('(')) { + base = parseExpectedExpression(); + expect(')'); + base.inParens = true; // XXX: quick and dirty. needed for validateVar + } else { + return null; + } + + // The suffix + var expression, identifier; + while (true) { + if (Punctuator === token.type) { + switch (token.value) { + case '[': + pushLocation(marker); + next(); + expression = parseExpectedExpression(); + base = finishNode(ast.indexExpression(base, expression)); + expect(']'); + break; + case '.': + pushLocation(marker); + next(); + identifier = parseIdentifier(); + base = finishNode(ast.memberExpression(base, '.', identifier)); + break; + case ':': + pushLocation(marker); + next(); + identifier = parseIdentifier(); + base = finishNode(ast.memberExpression(base, ':', identifier)); + // Once a : is found, this has to be a CallExpression, otherwise + // throw an error. + pushLocation(marker); + base = parseCallExpression(base); + break; + case '(': case '{': // args + pushLocation(marker); + base = parseCallExpression(base); + break; + default: + return base; + } + } else if (StringLiteral === token.type) { + pushLocation(marker); + base = parseCallExpression(base); + } else { + break; + } + } + + return base; + } + + // args ::= '(' [explist] ')' | tableconstructor | String + + function parseCallExpression(base) { + if (Punctuator === token.type) { + switch (token.value) { + case '(': + next(); + + // List of expressions + var expressions = []; + var expression = parseExpression(); + if (null != expression) expressions.push(expression); + while (consume(',')) { + expression = parseExpectedExpression(); + expressions.push(expression); + } + + expect(')'); + return finishNode(ast.callExpression(base, expressions)); + + case '{': + markLocation(); + next(); + var table = parseTableConstructor(); + return finishNode(ast.tableCallExpression(base, table)); + } + } else if (StringLiteral === token.type) { + return finishNode(ast.stringCallExpression(base, parsePrimaryExpression())); + } + + raiseUnexpectedToken('function arguments', token); + } + + // primary ::= String | Numeric | nil | true | false + // | functiondef | tableconstructor | '...' + + function parsePrimaryExpression() { + var literals = StringLiteral | NumericLiteral | BooleanLiteral | NilLiteral | VarargLiteral + , value = token.value + , type = token.type + , marker; + + if (trackLocations) marker = createLocationMarker(); + + if (type & literals) { + pushLocation(marker); + var raw = input.slice(token.range[0], token.range[1]); + next(); + return finishNode(ast.literal(type, value, raw)); + } else if (Keyword === type && 'function' === value) { + pushLocation(marker); + next(); + if (options.scope) createScope(); + return parseFunctionDeclaration(null); + } else if (consume('{')) { + pushLocation(marker); + return parseTableConstructor(); + } + } + + // Parser + // ------ + + // Export the main parser. + // + // - `wait` Hold parsing until end() is called. Defaults to false + // - `comments` Store comments. Defaults to true. + // - `scope` Track identifier scope. Defaults to false. + // - `locations` Store location information. Defaults to false. + // - `ranges` Store the start and end character locations. Defaults to + // false. + // - `onCreateNode` Callback which will be invoked when a syntax node is + // created. + // - `onCreateScope` Callback which will be invoked when a new scope is + // created. + // - `onDestroyScope` Callback which will be invoked when the current scope + // is destroyed. + // + // Example: + // + // var parser = require('luaparser'); + // parser.parse('i = 0'); + + exports.parse = parse; + + function parse(_input, _options) { + if ('undefined' === typeof _options && 'object' === typeof _input) { + _options = _input; + _input = undefined; + } + if (!_options) _options = {}; + + input = _input || ''; + options = extend(defaultOptions, _options); + + // Rewind the lexer + index = 0; + line = 1; + lineStart = 0; + length = input.length; + // When tracking identifier scope, initialize with an empty scope. + scopes = [[]]; + scopeDepth = 0; + globals = []; + locations = []; + + if (options.comments) comments = []; + if (!options.wait) return end(); + return exports; + } + + // Write to the source code buffer without beginning the parse. + exports.write = write; + + function write(_input) { + input += String(_input); + length = input.length; + return exports; + } + + // Send an EOF and begin parsing. + exports.end = end; + + function end(_input) { + if ('undefined' !== typeof _input) write(_input); + + // Ignore shebangs. + if (input && input.substr(0, 2) === '#!') input = input.replace(/^.*/, function (line) { + return line.replace(/./g, ' '); + }); + + length = input.length; + trackLocations = options.locations || options.ranges; + // Initialize with a lookahead token. + lookahead = lex(); + + var chunk = parseChunk(); + if (options.comments) chunk.comments = comments; + if (options.scope) chunk.globals = globals; + + if (locations.length > 0) + throw new Error('Location tracking failed. This is most likely a bug in luaparse'); + + return chunk; + } + +})); +/* vim: set sw=2 ts=2 et tw=79 : */ + + +/***/ }), + +/***/ 1418: +/***/ (function(module, exports, __nccwpck_require__) { + +/* module decorator */ module = __nccwpck_require__.nmd(module); +/* global exports:true, module:true, require:true, define:true, global:true */ + +(function (root, name, factory) { + 'use strict'; + + // Used to determine if values are of the language type `Object` + var objectTypes = { + 'function': true + , 'object': true + } + // Detect free variable `exports` + , freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports + // Detect free variable `module` + , freeModule = objectTypes["object"] && module && !module.nodeType && module + // Detect free variable `global`, from Node.js or Browserified code, and + // use it as `window` + , freeGlobal = freeExports && freeModule && typeof global === 'object' && global + // Detect the popular CommonJS extension `module.exports` + , moduleExports = freeModule && freeModule.exports === freeExports && freeExports; + + /* istanbul ignore else */ + if (freeGlobal && (freeGlobal.global === freeGlobal || + /* istanbul ignore next */ freeGlobal.window === freeGlobal || + /* istanbul ignore next */ freeGlobal.self === freeGlobal)) { + root = freeGlobal; + } + + // Some AMD build optimizers, like r.js, check for specific condition + // patterns like the following: + /* istanbul ignore if */ + if (typeof define === 'function' && + /* istanbul ignore next */ typeof define.amd === 'object' && + /* istanbul ignore next */ define.amd) { + // defined as an anonymous module. + define(['exports'], factory); + // In case the source has been processed and wrapped in a define module use + // the supplied `exports` object. + if (freeExports && moduleExports) factory(freeModule.exports); + } + // check for `exports` after `define` in case a build optimizer adds an + // `exports` object + else /* istanbul ignore else */ if (freeExports && freeModule) { + // in Node.js or RingoJS v0.8.0+ + /* istanbul ignore else */ + if (moduleExports) factory(freeModule.exports); + // in RingoJS v0.7.0- + else factory(freeExports); + } + // in a browser or Rhino + else { + factory((root[name] = {})); + } +}(this, 'luaparse', function (exports) { + 'use strict'; + + exports.version = '0.2.1'; + + var input, options, length, features; + + // Options can be set either globally on the parser object through + // defaultOptions, or during the parse call. + var defaultOptions = exports.defaultOptions = { + // Explicitly tell the parser when the input ends. + wait: false + // Store comments as an array in the chunk object. + , comments: true + // Track identifier scopes by adding an isLocal attribute to each + // identifier-node. + , scope: false + // Store location information on each syntax node as + // `loc: { start: { line, column }, end: { line, column } }`. + , locations: false + // Store the start and end character locations on each syntax node as + // `range: [start, end]`. + , ranges: false + // A callback which will be invoked when a syntax node has been completed. + // The node which has been created will be passed as the only parameter. + , onCreateNode: null + // A callback which will be invoked when a new scope is created. + , onCreateScope: null + // A callback which will be invoked when the current scope is destroyed. + , onDestroyScope: null + // A callback which will be invoked when a local variable is declared in the current scope. + // The variable's name will be passed as the only parameter + , onLocalDeclaration: null + // The version of Lua targeted by the parser (string; allowed values are + // '5.1', '5.2', '5.3'). + , luaVersion: '5.1' + }; + + // The available tokens expressed as enum flags so they can be checked with + // bitwise operations. + + var EOF = 1, StringLiteral = 2, Keyword = 4, Identifier = 8 + , NumericLiteral = 16, Punctuator = 32, BooleanLiteral = 64 + , NilLiteral = 128, VarargLiteral = 256; + + exports.tokenTypes = { EOF: EOF, StringLiteral: StringLiteral + , Keyword: Keyword, Identifier: Identifier, NumericLiteral: NumericLiteral + , Punctuator: Punctuator, BooleanLiteral: BooleanLiteral + , NilLiteral: NilLiteral, VarargLiteral: VarargLiteral + }; + + // As this parser is a bit different from luas own, the error messages + // will be different in some situations. + + var errors = exports.errors = { + unexpected: 'unexpected %1 \'%2\' near \'%3\'' + , unexpectedEOF: 'unexpected symbol near \'\'' + , expected: '\'%1\' expected near \'%2\'' + , expectedToken: '%1 expected near \'%2\'' + , unfinishedString: 'unfinished string near \'%1\'' + , malformedNumber: 'malformed number near \'%1\'' + , decimalEscapeTooLarge: 'decimal escape too large near \'%1\'' + , invalidEscape: 'invalid escape sequence near \'%1\'' + , hexadecimalDigitExpected: 'hexadecimal digit expected near \'%1\'' + , braceExpected: 'missing \'%1\' near \'%2\'' + , tooLargeCodepoint: 'UTF-8 value too large near \'%1\'' + , unfinishedLongString: 'unfinished long string (starting at line %1) near \'%2\'' + , unfinishedLongComment: 'unfinished long comment (starting at line %1) near \'%2\'' + , ambiguousSyntax: 'ambiguous syntax (function call x new statement) near \'%1\'' + , noLoopToBreak: 'no loop to break near \'%1\'' + , labelAlreadyDefined: 'label \'%1\' already defined on line %2' + , labelNotVisible: 'no visible label \'%1\' for ' + , gotoJumpInLocalScope: ' jumps into the scope of local \'%2\'' + , cannotUseVararg: 'cannot use \'...\' outside a vararg function near \'%1\'' + }; + + // ### Abstract Syntax Tree + // + // The default AST structure is inspired by the Mozilla Parser API but can + // easily be customized by overriding these functions. + + var ast = exports.ast = { + labelStatement: function(label) { + return { + type: 'LabelStatement' + , label: label + }; + } + + , breakStatement: function() { + return { + type: 'BreakStatement' + }; + } + + , gotoStatement: function(label) { + return { + type: 'GotoStatement' + , label: label + }; + } + + , returnStatement: function(args) { + return { + type: 'ReturnStatement' + , 'arguments': args + }; + } + + , ifStatement: function(clauses) { + return { + type: 'IfStatement' + , clauses: clauses + }; + } + , ifClause: function(condition, body) { + return { + type: 'IfClause' + , condition: condition + , body: body + }; + } + , elseifClause: function(condition, body) { + return { + type: 'ElseifClause' + , condition: condition + , body: body + }; + } + , elseClause: function(body) { + return { + type: 'ElseClause' + , body: body + }; + } + + , whileStatement: function(condition, body) { + return { + type: 'WhileStatement' + , condition: condition + , body: body + }; + } + + , doStatement: function(body) { + return { + type: 'DoStatement' + , body: body + }; + } + + , repeatStatement: function(condition, body) { + return { + type: 'RepeatStatement' + , condition: condition + , body: body + }; + } + + , localStatement: function(variables, init) { + return { + type: 'LocalStatement' + , variables: variables + , init: init + }; + } + + , assignmentStatement: function(variables, init) { + return { + type: 'AssignmentStatement' + , variables: variables + , init: init + }; + } + + , callStatement: function(expression) { + return { + type: 'CallStatement' + , expression: expression + }; + } + + , functionStatement: function(identifier, parameters, isLocal, body) { + return { + type: 'FunctionDeclaration' + , identifier: identifier + , isLocal: isLocal + , parameters: parameters + , body: body + }; + } + + , forNumericStatement: function(variable, start, end, step, body) { + return { + type: 'ForNumericStatement' + , variable: variable + , start: start + , end: end + , step: step + , body: body + }; + } + + , forGenericStatement: function(variables, iterators, body) { + return { + type: 'ForGenericStatement' + , variables: variables + , iterators: iterators + , body: body + }; + } + + , chunk: function(body) { + return { + type: 'Chunk' + , body: body + }; + } + + , identifier: function(name) { + return { + type: 'Identifier' + , name: name + }; + } + + , literal: function(type, value, raw) { + type = (type === StringLiteral) ? 'StringLiteral' + : (type === NumericLiteral) ? 'NumericLiteral' + : (type === BooleanLiteral) ? 'BooleanLiteral' + : (type === NilLiteral) ? 'NilLiteral' + : 'VarargLiteral'; + + return { + type: type + , value: value + , raw: raw + }; + } + + , tableKey: function(key, value) { + return { + type: 'TableKey' + , key: key + , value: value + }; + } + , tableKeyString: function(key, value) { + return { + type: 'TableKeyString' + , key: key + , value: value + }; + } + , tableValue: function(value) { + return { + type: 'TableValue' + , value: value + }; + } + + + , tableConstructorExpression: function(fields) { + return { + type: 'TableConstructorExpression' + , fields: fields + }; + } + , binaryExpression: function(operator, left, right) { + var type = ('and' === operator || 'or' === operator) ? + 'LogicalExpression' : + 'BinaryExpression'; + + return { + type: type + , operator: operator + , left: left + , right: right + }; + } + , unaryExpression: function(operator, argument) { + return { + type: 'UnaryExpression' + , operator: operator + , argument: argument + }; + } + , memberExpression: function(base, indexer, identifier) { + return { + type: 'MemberExpression' + , indexer: indexer + , identifier: identifier + , base: base + }; + } + + , indexExpression: function(base, index) { + return { + type: 'IndexExpression' + , base: base + , index: index + }; + } + + , indexesExpression: function(base, indexes) { + return { + type: 'IndexesExpression' + , base: base + , indexes: indexes + }; + } + + , callExpression: function(base, args) { + return { + type: 'CallExpression' + , base: base + , 'arguments': args + }; + } + + , tableCallExpression: function(base, args) { + return { + type: 'TableCallExpression' + , base: base + , 'arguments': args + }; + } + + , stringCallExpression: function(base, argument) { + return { + type: 'StringCallExpression' + , base: base + , argument: argument + }; + } + + , comment: function(value, raw) { + return { + type: 'Comment' + , value: value + , raw: raw + }; + } + + , shortFunctionDefinition: function(parameters, expression) { + return { + type: 'ShortFunctionDefinition' + , parameters: parameters + , expression: expression + }; + } + }; + + // Wrap up the node object. + + function finishNode(node) { + // Pop a `Marker` off the location-array and attach its location data. + if (trackLocations) { + var location = locations.pop(); + location.complete(); + location.bless(node); + } + if (options.onCreateNode) options.onCreateNode(node); + return node; + } + + + // Helpers + // ------- + + var slice = Array.prototype.slice + , toString = Object.prototype.toString + ; + + var indexOf = /* istanbul ignore next */ function (array, element) { + for (var i = 0, length = array.length; i < length; ++i) { + if (array[i] === element) return i; + } + return -1; + }; + + /* istanbul ignore else */ + if (Array.prototype.indexOf) + indexOf = function (array, element) { + return array.indexOf(element); + }; + + // Iterate through an array of objects and return the index of an object + // with a matching property. + + function indexOfObject(array, property, element) { + for (var i = 0, length = array.length; i < length; ++i) { + if (array[i][property] === element) return i; + } + return -1; + } + + // A sprintf implementation using %index (beginning at 1) to input + // arguments in the format string. + // + // Example: + // + // // Unexpected function in token + // sprintf('Unexpected %2 in %1.', 'token', 'function'); + + function sprintf(format) { + var args = slice.call(arguments, 1); + format = format.replace(/%(\d)/g, function (match, index) { + return '' + args[index - 1] || /* istanbul ignore next */ ''; + }); + return format; + } + + // Polyfill for `Object.assign`. + + var assign = /* istanbul ignore next */ function (dest) { + var args = slice.call(arguments, 1) + , src, prop; + + for (var i = 0, length = args.length; i < length; ++i) { + src = args[i]; + for (prop in src) + /* istanbul ignore else */ + if (Object.prototype.hasOwnProperty.call(src, prop)) { + dest[prop] = src[prop]; + } + } + + return dest; + }; + + /* istanbul ignore else */ + if (Object.assign) + assign = Object.assign; + + // ### Error functions + + // XXX: Eliminate this function and change the error type to be different from SyntaxError. + // This will unfortunately be a breaking change, because some downstream users depend + // on the error thrown being an instance of SyntaxError. For example, the Ace editor: + // + + function fixupError(e) { + /* istanbul ignore if */ + if (!Object.create) + return e; + return Object.create(e, { + 'line': { 'writable': true, value: e.line }, + 'index': { 'writable': true, value: e.index }, + 'column': { 'writable': true, value: e.column } + }); + } + + // #### Raise an exception. + // + // Raise an exception by passing a token, a string format and its paramters. + // + // The passed tokens location will automatically be added to the error + // message if it exists, if not it will default to the lexers current + // position. + // + // Example: + // + // // [1:0] expected [ near ( + // raise(token, "expected %1 near %2", '[', token.value); + + function raise(token) { + var message = sprintf.apply(null, slice.call(arguments, 1)) + , error, col; + + if (token === null || typeof token.line === 'undefined') { + col = index - lineStart + 1; + error = fixupError(new SyntaxError(sprintf('[%1:%2] %3', line, col, message))); + error.index = index; + error.line = line; + error.column = col; + } else { + col = token.range[0] - token.lineStart; + error = fixupError(new SyntaxError(sprintf('[%1:%2] %3', token.line, col, message))); + error.line = token.line; + error.index = token.range[0]; + error.column = col; + } + throw error; + } + + // #### Raise an unexpected token error. + // + // Example: + // + // // expected near '0' + // raiseUnexpectedToken('', token); + + function raiseUnexpectedToken(type, token) { + raise(token, errors.expectedToken, type, token.value); + } + + // #### Raise a general unexpected error + // + // Usage should pass either a token object or a symbol string which was + // expected. We can also specify a nearby token such as , this will + // default to the currently active token. + // + // Example: + // + // // Unexpected symbol 'end' near '' + // unexpected(token); + // + // If there's no token in the buffer it means we have reached . + + function unexpected(found) { + var near = lookahead.value; + if ('undefined' !== typeof found.type) { + var type; + switch (found.type) { + case StringLiteral: type = 'string'; break; + case Keyword: type = 'keyword'; break; + case Identifier: type = 'identifier'; break; + case NumericLiteral: type = 'number'; break; + case Punctuator: type = 'symbol'; break; + case BooleanLiteral: type = 'boolean'; break; + case NilLiteral: + return raise(found, errors.unexpected, 'symbol', 'nil', near); + case EOF: + return raise(found, errors.unexpectedEOF); + } + return raise(found, errors.unexpected, type, found.value, near); + } + return raise(found, errors.unexpected, 'symbol', found, near); + } + + // Lexer + // ----- + // + // The lexer, or the tokenizer reads the input string character by character + // and derives a token left-right. To be as efficient as possible the lexer + // prioritizes the common cases such as identifiers. It also works with + // character codes instead of characters as string comparisons was the + // biggest bottleneck of the parser. + // + // If `options.comments` is enabled, all comments encountered will be stored + // in an array which later will be appended to the chunk object. If disabled, + // they will simply be disregarded. + // + // When the lexer has derived a valid token, it will be returned as an object + // containing its value and as well as its position in the input string (this + // is always enabled to provide proper debug messages). + // + // `lex()` starts lexing and returns the following token in the stream. + + var index + , token + , previousToken + , lookahead + , comments + , tokenStart + , line + , lineStart; + + exports.lex = lex; + + function lex() { + skipWhiteSpace(); + + // Skip comments beginning with -- + while (45 === input.charCodeAt(index) && + 45 === input.charCodeAt(index + 1)) { + scanComment(); + skipWhiteSpace(); + } + if (index >= length) return { + type : EOF + , value: '' + , line: line + , lineStart: lineStart + , range: [index, index] + }; + + var charCode = input.charCodeAt(index) + , next = input.charCodeAt(index + 1); + + // Memorize the range index where the token begins. + tokenStart = index; + if (isIdentifierStart(charCode)) return scanIdentifierOrKeyword(); + + switch (charCode) { + case 39: case 34: // '" + return scanStringLiteral(); + + case 48: case 49: case 50: case 51: case 52: case 53: + case 54: case 55: case 56: case 57: // 0-9 + return scanNumericLiteral(); + + case 46: // . + // If the dot is followed by a digit it's a float. + if (isDecDigit(next)) return scanNumericLiteral(); + if (46 === next) { + if (46 === input.charCodeAt(index + 2)) return scanVarargLiteral(); + return scanPunctuator('..'); + } + return scanPunctuator('.'); + + case 61: // = + if (61 === next) return scanPunctuator('=='); + return scanPunctuator('='); + + case 62: // > + if (features.bitwiseOperators) + if (62 === next) return scanPunctuator('>>'); + if (61 === next) return scanPunctuator('>='); + return scanPunctuator('>'); + + case 60: // < + if (features.bitwiseOperators) + if (60 === next) return scanPunctuator('<<'); + if (61 === next) return scanPunctuator('<='); + return scanPunctuator('<'); + + case 126: // ~ + if (61 === next) return scanPunctuator('~='); + if (!features.bitwiseOperators) + break; + return scanPunctuator('~'); + + case 33: + if (61 === next) return scanPunctuator('!='); + break; + + case 58: // : + if (features.labels) + if (58 === next) return scanPunctuator('::'); + return scanPunctuator(':'); + + case 91: // [ + // Check for a multiline string, they begin with [= or [[ + if (91 === next || 61 === next) return scanLongStringLiteral(); + return scanPunctuator('['); + + case 47: // / + // Check for integer division op (//) + if (features.integerDivision) + if (47 === next) return scanPunctuator('//'); + return scanPunctuator('/'); + + case 38: // & + if (!features.bitwiseOperators) + break; + /* fall through */ + + case 124: // | + + /* fall through */ + case 42: case 94: case 37: case 44: case 123: case 125: + case 93: case 40: case 41: case 59: case 35: case 45: + case 43: // * ^ % , { } ] ( ) ; # - + + return scanPunctuator(input.charAt(index)); + } + + return unexpected(input.charAt(index)); + } + + // Whitespace has no semantic meaning in lua so simply skip ahead while + // tracking the encounted newlines. Any kind of eol sequence is counted as a + // single line. + + function consumeEOL() { + var charCode = input.charCodeAt(index) + , peekCharCode = input.charCodeAt(index + 1); + + if (isLineTerminator(charCode)) { + // Count \n\r and \r\n as one newline. + if (10 === charCode && 13 === peekCharCode) ++index; + if (13 === charCode && 10 === peekCharCode) ++index; + ++line; + lineStart = ++index; + + return true; + } + return false; + } + + function skipWhiteSpace() { + while (index < length) { + var charCode = input.charCodeAt(index); + if (isWhiteSpace(charCode)) { + ++index; + } else if (!consumeEOL()) { + break; + } + } + } + + function encodeUTF8(codepoint) { + if (codepoint < 0x80) { + return String.fromCharCode(codepoint); + } else if (codepoint < 0x800) { + return String.fromCharCode( + 0xc0 | (codepoint >> 6) , + 0x80 | ( codepoint & 0x3f) + ); + } else if (codepoint < 0x10000) { + return String.fromCharCode( + 0xe0 | (codepoint >> 12) , + 0x80 | ((codepoint >> 6) & 0x3f), + 0x80 | ( codepoint & 0x3f) + ); + } else if (codepoint < 0x110000) { + return String.fromCharCode( + 0xf0 | (codepoint >> 18) , + 0x80 | ((codepoint >> 12) & 0x3f), + 0x80 | ((codepoint >> 6) & 0x3f), + 0x80 | ( codepoint & 0x3f) + ); + } else { + return null; + } + } + + // This function takes a JavaScript string, encodes it in WTF-8 and + // reinterprets the resulting code units as code points; i.e. it encodes + // the string in what was the original meaning of WTF-8. + // + // For a detailed rationale, see the README.md file, section + // "Note on character encodings". + + function fixupHighCharacters(s) { + return s.replace(/[\ud800-\udbff][\udc00-\udfff]|[^\x00-\x7f]/g, function (m) { + if (m.length === 1) + return encodeUTF8(m.charCodeAt(0)); + return encodeUTF8(0x10000 + (((m.charCodeAt(0) & 0x3ff) << 10) | (m.charCodeAt(1) & 0x3ff))); + }); + } + + // Identifiers, keywords, booleans and nil all look the same syntax wise. We + // simply go through them one by one and defaulting to an identifier if no + // previous case matched. + + function scanIdentifierOrKeyword() { + var value, type; + + // Slicing the input string is prefered before string concatenation in a + // loop for performance reasons. + while (isIdentifierPart(input.charCodeAt(++index))); + value = fixupHighCharacters(input.slice(tokenStart, index)); + + // Decide on the token type and possibly cast the value. + if (isKeyword(value)) { + type = Keyword; + } else if ('true' === value || 'false' === value) { + type = BooleanLiteral; + value = ('true' === value); + } else if ('nil' === value) { + type = NilLiteral; + value = null; + } else { + type = Identifier; + } + + return { + type: type + , value: value + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Once a punctuator reaches this function it should already have been + // validated so we simply return it as a token. + + function scanPunctuator(value) { + index += value.length; + return { + type: Punctuator + , value: value + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // A vararg literal consists of three dots. + + function scanVarargLiteral() { + index += 3; + return { + type: VarargLiteral + , value: '...' + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Find the string literal by matching the delimiter marks used. + + function scanStringLiteral() { + var delimiter = input.charCodeAt(index++) + , beginLine = line + , beginLineStart = lineStart + , stringStart = index + , string = '' + , charCode; + + for (;;) { + charCode = input.charCodeAt(index++); + if (delimiter === charCode) break; + // EOF or `\n` terminates a string literal. If we haven't found the + // ending delimiter by now, raise an exception. + if (index > length || isLineTerminator(charCode)) { + string += input.slice(stringStart, index - 1); + raise(null, errors.unfinishedString, String.fromCharCode(delimiter) + string); + } + if (92 === charCode) { // backslash + string += fixupHighCharacters(input.slice(stringStart, index - 1)) + readEscapeSequence(); + stringStart = index; + } + } + string += fixupHighCharacters(input.slice(stringStart, index - 1)); + + return { + type: StringLiteral + , value: string + , line: beginLine + , lineStart: beginLineStart + , lastLine: line + , lastLineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Expect a multiline string literal and return it as a regular string + // literal, if it doesn't validate into a valid multiline string, throw an + // exception. + + function scanLongStringLiteral() { + var beginLine = line + , beginLineStart = lineStart + , string = readLongString(false); + // Fail if it's not a multiline literal. + if (false === string) raise(token, errors.expected, '[', token.value); + + return { + type: StringLiteral + , value: fixupHighCharacters(string) + , line: beginLine + , lineStart: beginLineStart + , lastLine: line + , lastLineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Numeric literals will be returned as floating-point numbers instead of + // strings. The raw value should be retrieved from slicing the input string + // later on in the process. + // + // If a hexadecimal number is encountered, it will be converted. + + function scanNumericLiteral() { + var character = input.charAt(index) + , next = input.charAt(index + 1); + + var value = ('0' === character && 'xX'.indexOf(next || null) >= 0) ? + readHexLiteral() : readDecLiteral(); + + return { + type: NumericLiteral + , value: value + , line: line + , lineStart: lineStart + , range: [tokenStart, index] + }; + } + + // Lua hexadecimals have an optional fraction part and an optional binary + // exoponent part. These are not included in JavaScript so we will compute + // all three parts separately and then sum them up at the end of the function + // with the following algorithm. + // + // Digit := toDec(digit) + // Fraction := toDec(fraction) / 16 ^ fractionCount + // BinaryExp := 2 ^ binaryExp + // Number := ( Digit + Fraction ) * BinaryExp + + function readHexLiteral() { + var fraction = 0 // defaults to 0 as it gets summed + , binaryExponent = 1 // defaults to 1 as it gets multiplied + , binarySign = 1 // positive + , digit, fractionStart, exponentStart, digitStart; + + digitStart = index += 2; // Skip 0x part + + // A minimum of one hex digit is required. + if (!isHexDigit(input.charCodeAt(index))) + raise(null, errors.malformedNumber, input.slice(tokenStart, index)); + + while (isHexDigit(input.charCodeAt(index))) ++index; + // Convert the hexadecimal digit to base 10. + digit = parseInt(input.slice(digitStart, index), 16); + + // Fraction part i optional. + if ('.' === input.charAt(index)) { + fractionStart = ++index; + + while (isHexDigit(input.charCodeAt(index))) ++index; + fraction = input.slice(fractionStart, index); + + // Empty fraction parts should default to 0, others should be converted + // 0.x form so we can use summation at the end. + fraction = (fractionStart === index) ? 0 + : parseInt(fraction, 16) / Math.pow(16, index - fractionStart); + } + + // Binary exponents are optional + if ('pP'.indexOf(input.charAt(index) || null) >= 0) { + ++index; + + // Sign part is optional and defaults to 1 (positive). + if ('+-'.indexOf(input.charAt(index) || null) >= 0) + binarySign = ('+' === input.charAt(index++)) ? 1 : -1; + + exponentStart = index; + + // The binary exponent sign requires a decimal digit. + if (!isDecDigit(input.charCodeAt(index))) + raise(null, errors.malformedNumber, input.slice(tokenStart, index)); + + while (isDecDigit(input.charCodeAt(index))) ++index; + binaryExponent = input.slice(exponentStart, index); + + // Calculate the binary exponent of the number. + binaryExponent = Math.pow(2, binaryExponent * binarySign); + } + + return (digit + fraction) * binaryExponent; + } + + // Decimal numbers are exactly the same in Lua and in JavaScript, because of + // this we check where the token ends and then parse it with native + // functions. + + function readDecLiteral() { + while (isDecDigit(input.charCodeAt(index))) ++index; + // Fraction part is optional + if ('.' === input.charAt(index)) { + ++index; + // Fraction part defaults to 0 + while (isDecDigit(input.charCodeAt(index))) ++index; + } + // Exponent part is optional. + if ('eE'.indexOf(input.charAt(index) || null) >= 0) { + ++index; + // Sign part is optional. + if ('+-'.indexOf(input.charAt(index) || null) >= 0) ++index; + // An exponent is required to contain at least one decimal digit. + if (!isDecDigit(input.charCodeAt(index))) + raise(null, errors.malformedNumber, input.slice(tokenStart, index)); + + while (isDecDigit(input.charCodeAt(index))) ++index; + } + + return parseFloat(input.slice(tokenStart, index)); + } + + function readUnicodeEscapeSequence() { + var sequenceStart = index++; + + if (input.charAt(index++) !== '{') + raise(null, errors.braceExpected, '{', '\\' + input.slice(sequenceStart, index)); + if (!isHexDigit(input.charCodeAt(index))) + raise(null, errors.hexadecimalDigitExpected, '\\' + input.slice(sequenceStart, index)); + + while (input.charCodeAt(index) === 0x30) ++index; + var escStart = index; + + while (isHexDigit(input.charCodeAt(index))) { + ++index; + if (index - escStart > 6) + raise(null, errors.tooLargeCodepoint, '\\' + input.slice(sequenceStart, index)); + } + + var b = input.charAt(index++); + if (b !== '}') { + if ((b === '"') || (b === "'")) + raise(null, errors.braceExpected, '}', '\\' + input.slice(sequenceStart, index--)); + else + raise(null, errors.hexadecimalDigitExpected, '\\' + input.slice(sequenceStart, index)); + } + + var codepoint = parseInt(input.slice(escStart, index - 1), 16); + + codepoint = encodeUTF8(codepoint); + if (codepoint === null) { + raise(null, errors.tooLargeCodepoint, '\\' + input.slice(sequenceStart, index)); + } + return codepoint; + } + + // Translate escape sequences to the actual characters. + function readEscapeSequence() { + var sequenceStart = index; + switch (input.charAt(index)) { + // Lua allow the following escape sequences. + case 'a': ++index; return '\x07'; + case 'n': ++index; return '\n'; + case 'r': ++index; return '\r'; + case 't': ++index; return '\t'; + case 'v': ++index; return '\x0b'; + case 'b': ++index; return '\b'; + case 'f': ++index; return '\f'; + + // Backslash at the end of the line. We treat all line endings as equivalent, + // and as representing the [LF] character (code 10). Lua 5.1 through 5.3 + // have been verified to behave the same way. + case '\r': + case '\n': + consumeEOL(); + return '\n'; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + // \ddd, where ddd is a sequence of up to three decimal digits. + while (isDecDigit(input.charCodeAt(index)) && index - sequenceStart < 3) ++index; + + var ddd = parseInt(input.slice(sequenceStart, index), 10); + if (ddd > 255) { + raise(null, errors.decimalEscapeTooLarge, '\\' + ddd); + } + return String.fromCharCode(ddd); + + case 'z': + if (features.skipWhitespaceEscape) { + ++index; + skipWhiteSpace(); + return ''; + } + break; + + case 'x': + if (features.hexEscapes) { + // \xXX, where XX is a sequence of exactly two hexadecimal digits + if (isHexDigit(input.charCodeAt(index + 1)) && + isHexDigit(input.charCodeAt(index + 2))) { + index += 3; + return String.fromCharCode(parseInt(input.slice(sequenceStart + 1, index), 16)); + } + raise(null, errors.hexadecimalDigitExpected, '\\' + input.slice(sequenceStart, index + 2)); + } + break; + + case 'u': + if (features.unicodeEscapes) + return readUnicodeEscapeSequence(); + break; + + case '\\': case '"': case "'": + return input.charAt(index++); + } + + if (features.strictEscapes) + raise(null, errors.invalidEscape, '\\' + input.slice(sequenceStart, index + 1)); + return input.charAt(index++); + } + + // Comments begin with -- after which it will be decided if they are + // multiline comments or not. + // + // The multiline functionality works the exact same way as with string + // literals so we reuse the functionality. + + function scanComment() { + tokenStart = index; + index += 2; // -- + + var character = input.charAt(index) + , content = '' + , isLong = false + , commentStart = index + , lineStartComment = lineStart + , lineComment = line; + + if ('[' === character) { + content = readLongString(true); + // This wasn't a multiline comment after all. + if (false === content) content = character; + else isLong = true; + } + // Scan until next line as long as it's not a multiline comment. + if (!isLong) { + while (index < length) { + if (isLineTerminator(input.charCodeAt(index))) break; + ++index; + } + if (options.comments) content = input.slice(commentStart, index); + } + + if (options.comments) { + var node = ast.comment(content, input.slice(tokenStart, index)); + + // `Marker`s depend on tokens available in the parser and as comments are + // intercepted in the lexer all location data is set manually. + if (options.locations) { + node.loc = { + start: { line: lineComment, column: tokenStart - lineStartComment } + , end: { line: line, column: index - lineStart } + }; + } + if (options.ranges) { + node.range = [tokenStart, index]; + } + if (options.onCreateNode) options.onCreateNode(node); + comments.push(node); + } + } + + // Read a multiline string by calculating the depth of `=` characters and + // then appending until an equal depth is found. + + function readLongString(isComment) { + var level = 0 + , content = '' + , terminator = false + , character, stringStart, firstLine = line; + + ++index; // [ + + // Calculate the depth of the comment. + while ('=' === input.charAt(index + level)) ++level; + // Exit, this is not a long string afterall. + if ('[' !== input.charAt(index + level)) return false; + + index += level + 1; + + // If the first character is a newline, ignore it and begin on next line. + if (isLineTerminator(input.charCodeAt(index))) consumeEOL(); + + stringStart = index; + while (index < length) { + // To keep track of line numbers run the `consumeEOL()` which increments + // its counter. + while (isLineTerminator(input.charCodeAt(index))) consumeEOL(); + + character = input.charAt(index++); + + // Once the delimiter is found, iterate through the depth count and see + // if it matches. + if (']' === character) { + terminator = true; + for (var i = 0; i < level; ++i) { + if ('=' !== input.charAt(index + i)) terminator = false; + } + if (']' !== input.charAt(index + level)) terminator = false; + } + + // We reached the end of the multiline string. Get out now. + if (terminator) { + content += input.slice(stringStart, index - 1); + index += level + 1; + return content; + } + } + + raise(null, isComment ? + errors.unfinishedLongComment : + errors.unfinishedLongString, + firstLine, ''); + } + + // ## Lex functions and helpers. + + // Read the next token. + // + // This is actually done by setting the current token to the lookahead and + // reading in the new lookahead token. + + function next() { + previousToken = token; + token = lookahead; + lookahead = lex(); + } + + // Consume a token if its value matches. Once consumed or not, return the + // success of the operation. + + function consume(value) { + if (value === token.value) { + next(); + return true; + } + return false; + } + + // Expect the next token value to match. If not, throw an exception. + + function expect(value) { + if (value === token.value) next(); + else raise(token, errors.expected, value, token.value); + } + + // ### Validation functions + + function isWhiteSpace(charCode) { + return 9 === charCode || 32 === charCode || 0xB === charCode || 0xC === charCode; + } + + function isLineTerminator(charCode) { + return 10 === charCode || 13 === charCode; + } + + function isDecDigit(charCode) { + return charCode >= 48 && charCode <= 57; + } + + function isHexDigit(charCode) { + return (charCode >= 48 && charCode <= 57) || (charCode >= 97 && charCode <= 102) || (charCode >= 65 && charCode <= 70); + } + + // From [Lua 5.2](http://www.lua.org/manual/5.2/manual.html#8.1) onwards + // identifiers cannot use 'locale-dependent' letters (i.e. dependent on the C locale). + // On the other hand, LuaJIT allows arbitrary octets ≥ 128 in identifiers. + + function isIdentifierStart(charCode) { + if ((charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122) || 95 === charCode) + return true; + if (features.extendedIdentifiers && charCode >= 128) + return true; + return false; + } + + function isIdentifierPart(charCode) { + if ((charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122) || 95 === charCode || (charCode >= 48 && charCode <= 57)) + return true; + if (features.extendedIdentifiers && charCode >= 128) + return true; + return false; + } + + // [3.1 Lexical Conventions](http://www.lua.org/manual/5.2/manual.html#3.1) + // + // `true`, `false` and `nil` will not be considered keywords, but literals. + + function isKeyword(id) { + switch (id.length) { + case 2: + return 'do' === id || 'if' === id || 'in' === id || 'or' === id; + case 3: + return 'and' === id || 'end' === id || 'for' === id || 'not' === id; + case 4: + if ('else' === id || 'then' === id) + return true; + if (features.labels && !features.contextualGoto) + return ('goto' === id); + return false; + case 5: + return 'break' === id || 'local' === id || 'until' === id || 'while' === id; + case 6: + return 'elseif' === id || 'repeat' === id || 'return' === id; + case 8: + return 'function' === id; + } + return false; + } + + function isUnary(token) { + if (Punctuator === token.type) return '#-~'.indexOf(token.value) >= 0; + if (Keyword === token.type) return 'not' === token.value; + return false; + } + + // Check if the token syntactically closes a block. + + function isBlockFollow(token) { + if (EOF === token.type) return true; + if (Keyword !== token.type) return false; + switch (token.value) { + case 'else': case 'elseif': + case 'end': case 'until': + return true; + default: + return false; + } + } + + // Scope + // ----- + + // Store each block scope as a an array of identifier names. Each scope is + // stored in an FILO-array. + var scopes + // The current scope index + , scopeDepth + // A list of all global identifier nodes. + , globals; + + // Create a new scope inheriting all declarations from the previous scope. + function createScope() { + var scope = Array.apply(null, scopes[scopeDepth++]); + scopes.push(scope); + if (options.onCreateScope) options.onCreateScope(); + } + + // Exit and remove the current scope. + function destroyScope() { + var scope = scopes.pop(); + --scopeDepth; + if (options.onDestroyScope) options.onDestroyScope(); + } + + // Add identifier name to the current scope if it doesnt already exist. + function scopeIdentifierName(name) { + if (options.onLocalDeclaration) options.onLocalDeclaration(name); + if (-1 !== indexOf(scopes[scopeDepth], name)) return; + scopes[scopeDepth].push(name); + } + + // Add identifier to the current scope + function scopeIdentifier(node) { + scopeIdentifierName(node.name); + attachScope(node, true); + } + + // Attach scope information to node. If the node is global, store it in the + // globals array so we can return the information to the user. + function attachScope(node, isLocal) { + if (!isLocal && -1 === indexOfObject(globals, 'name', node.name)) + globals.push(node); + + node.isLocal = isLocal; + } + + // Is the identifier name available in this scope. + function scopeHasName(name) { + return (-1 !== indexOf(scopes[scopeDepth], name)); + } + + // Location tracking + // ----------------- + // + // Locations are stored in FILO-array as a `Marker` object consisting of both + // `loc` and `range` data. Once a `Marker` is popped off the list an end + // location is added and the data is attached to a syntax node. + + var locations = [] + , trackLocations; + + function createLocationMarker() { + return new Marker(token); + } + + function Marker(token) { + if (options.locations) { + this.loc = { + start: { + line: token.line + , column: token.range[0] - token.lineStart + } + , end: { + line: 0 + , column: 0 + } + }; + } + if (options.ranges) this.range = [token.range[0], 0]; + } + + // Complete the location data stored in the `Marker` by adding the location + // of the *previous token* as an end location. + Marker.prototype.complete = function() { + if (options.locations) { + this.loc.end.line = previousToken.lastLine || previousToken.line; + this.loc.end.column = previousToken.range[1] - (previousToken.lastLineStart || previousToken.lineStart); + } + if (options.ranges) { + this.range[1] = previousToken.range[1]; + } + }; + + Marker.prototype.bless = function (node) { + if (this.loc) { + var loc = this.loc; + node.loc = { + start: { + line: loc.start.line, + column: loc.start.column + }, + end: { + line: loc.end.line, + column: loc.end.column + } + }; + } + if (this.range) { + node.range = [ + this.range[0], + this.range[1] + ]; + } + }; + + // Create a new `Marker` and add it to the FILO-array. + function markLocation() { + if (trackLocations) locations.push(createLocationMarker()); + } + + // Push an arbitrary `Marker` object onto the FILO-array. + function pushLocation(marker) { + if (trackLocations) locations.push(marker); + } + + // Control flow tracking + // --------------------- + // A context object that validates loop breaks and `goto`-based control flow. + + function FullFlowContext() { + this.scopes = []; + this.pendingGotos = []; + } + + FullFlowContext.prototype.isInLoop = function () { + var i = this.scopes.length; + while (i --> 0) { + if (this.scopes[i].isLoop) + return true; + } + return false; + }; + + FullFlowContext.prototype.pushScope = function (isLoop) { + var scope = { + labels: {}, + locals: [], + deferredGotos: [], + isLoop: !!isLoop + }; + this.scopes.push(scope); + }; + + FullFlowContext.prototype.popScope = function () { + for (var i = 0; i < this.pendingGotos.length; ++i) { + var theGoto = this.pendingGotos[i]; + if (theGoto.maxDepth >= this.scopes.length) + if (--theGoto.maxDepth <= 0) + raise(theGoto.token, errors.labelNotVisible, theGoto.target); + } + + this.scopes.pop(); + }; + + FullFlowContext.prototype.addGoto = function (target, token) { + var localCounts = []; + + for (var i = 0; i < this.scopes.length; ++i) { + var scope = this.scopes[i]; + localCounts.push(scope.locals.length); + if (Object.prototype.hasOwnProperty.call(scope.labels, target)) + return; + } + + this.pendingGotos.push({ + maxDepth: this.scopes.length, + target: target, + token: token, + localCounts: localCounts + }); + }; + + FullFlowContext.prototype.addLabel = function (name, token) { + var scope = this.currentScope(); + + if (Object.prototype.hasOwnProperty.call(scope.labels, name)) { + raise(token, errors.labelAlreadyDefined, name, scope.labels[name].line); + } else { + var newGotos = []; + + for (var i = 0; i < this.pendingGotos.length; ++i) { + var theGoto = this.pendingGotos[i]; + + if (theGoto.maxDepth >= this.scopes.length && theGoto.target === name) { + if (theGoto.localCounts[this.scopes.length - 1] < scope.locals.length) { + scope.deferredGotos.push(theGoto); + } + continue; + } + + newGotos.push(theGoto); + } + + this.pendingGotos = newGotos; + } + + scope.labels[name] = { + localCount: scope.locals.length, + line: token.line + }; + }; + + FullFlowContext.prototype.addLocal = function (name, token) { + this.currentScope().locals.push({ + name: name, + token: token + }); + }; + + FullFlowContext.prototype.currentScope = function () { + return this.scopes[this.scopes.length - 1]; + }; + + FullFlowContext.prototype.raiseDeferredErrors = function () { + var scope = this.currentScope(); + var bads = scope.deferredGotos; + for (var i = 0; i < bads.length; ++i) { + var theGoto = bads[i]; + raise(theGoto.token, errors.gotoJumpInLocalScope, theGoto.target, scope.locals[theGoto.localCounts[this.scopes.length - 1]].name); + } + // Would be dead code currently, but may be useful later + // if (bads.length) + // scope.deferredGotos = []; + }; + + // Simplified context that only checks the validity of loop breaks. + + function LoopFlowContext() { + this.level = 0; + this.loopLevels = []; + } + + LoopFlowContext.prototype.isInLoop = function () { + return !!this.loopLevels.length; + }; + + LoopFlowContext.prototype.pushScope = function (isLoop) { + ++this.level; + if (isLoop) + this.loopLevels.push(this.level); + }; + + LoopFlowContext.prototype.popScope = function () { + var levels = this.loopLevels; + var levlen = levels.length; + if (levlen) { + if (levels[levlen - 1] === this.level) + levels.pop(); + } + --this.level; + }; + + LoopFlowContext.prototype.addGoto = + LoopFlowContext.prototype.addLabel = + /* istanbul ignore next */ + function () { throw new Error('This should never happen'); }; + + LoopFlowContext.prototype.addLocal = + LoopFlowContext.prototype.raiseDeferredErrors = + function () {}; + + function makeFlowContext() { + return features.labels ? new FullFlowContext() : new LoopFlowContext(); + } + + // Parse functions + // --------------- + + // Chunk is the main program object. Syntactically it's the same as a block. + // + // chunk ::= block + + function parseChunk() { + next(); + markLocation(); + if (options.scope) createScope(); + var flowContext = makeFlowContext(); + flowContext.allowVararg = true; + flowContext.pushScope(); + var body = parseBlock(flowContext); + flowContext.popScope(); + if (options.scope) destroyScope(); + if (EOF !== token.type) unexpected(token); + // If the body is empty no previousToken exists when finishNode runs. + if (trackLocations && !body.length) previousToken = token; + return finishNode(ast.chunk(body)); + } + + // A block contains a list of statements with an optional return statement + // as its last statement. + // + // block ::= {stat} [retstat] + + function parseBlock(flowContext) { + var block = [] + , statement; + + while (!isBlockFollow(token)) { + // Return has to be the last statement in a block. + // Likewise 'break' in Lua older than 5.2 + if ('return' === token.value || (!features.relaxedBreak && 'break' === token.value)) { + block.push(parseStatement(flowContext)); + break; + } + statement = parseStatement(flowContext); + consume(';'); + // Statements are only added if they are returned, this allows us to + // ignore some statements, such as EmptyStatement. + if (statement) block.push(statement); + } + + // Doesn't really need an ast node + return block; + } + + // There are two types of statements, simple and compound. + // + // statement ::= break | goto | do | while | repeat | return + // | if | for | function | local | label | assignment + // | functioncall | ';' + + function parseStatement(flowContext) { + markLocation(); + + if (Punctuator === token.type) { + if (consume('::')) return parseLabelStatement(flowContext); + } + + // When a `;` is encounted, simply eat it without storing it. + if (features.emptyStatement) { + if (consume(';')) { + if (trackLocations) locations.pop(); + return; + } + } + + flowContext.raiseDeferredErrors(); + + if (Keyword === token.type) { + switch (token.value) { + case 'local': next(); return parseLocalStatement(flowContext); + case 'if': next(); return parseIfStatement(flowContext); + case 'return': next(); return parseReturnStatement(flowContext); + case 'function': next(); + var name = parseFunctionName(); + return parseFunctionDeclaration(name); + case 'while': next(); return parseWhileStatement(flowContext); + case 'for': next(); return parseForStatement(flowContext); + case 'repeat': next(); return parseRepeatStatement(flowContext); + case 'break': next(); + if (!flowContext.isInLoop()) + raise(token, errors.noLoopToBreak, token.value); + return parseBreakStatement(); + case 'do': next(); return parseDoStatement(flowContext); + case 'goto': next(); return parseGotoStatement(flowContext); + } + } + + if (features.contextualGoto && + token.type === Identifier && token.value === 'goto' && + lookahead.type === Identifier && lookahead.value !== 'goto') { + next(); return parseGotoStatement(flowContext); + } + + // Assignments memorizes the location and pushes it manually for wrapper nodes. + if (trackLocations) locations.pop(); + + return parseAssignmentOrCallStatement(flowContext); + } + + // ## Statements + + // label ::= '::' Name '::' + + function parseLabelStatement(flowContext) { + var nameToken = token + , label = parseIdentifier(); + + if (options.scope) { + scopeIdentifierName('::' + nameToken.value + '::'); + attachScope(label, true); + } + + expect('::'); + + flowContext.addLabel(nameToken.value, nameToken); + return finishNode(ast.labelStatement(label)); + } + + // break ::= 'break' + + function parseBreakStatement() { + return finishNode(ast.breakStatement()); + } + + // goto ::= 'goto' Name + + function parseGotoStatement(flowContext) { + var name = token.value + , gotoToken = previousToken + , label = parseIdentifier(); + + flowContext.addGoto(name, gotoToken); + return finishNode(ast.gotoStatement(label)); + } + + // do ::= 'do' block 'end' + + function parseDoStatement(flowContext) { + if (options.scope) createScope(); + flowContext.pushScope(); + var body = parseBlock(flowContext); + flowContext.popScope(); + if (options.scope) destroyScope(); + expect('end'); + return finishNode(ast.doStatement(body)); + } + + // while ::= 'while' exp 'do' block 'end' + + function parseWhileStatement(flowContext) { + var condition = parseExpectedExpression(flowContext); + expect('do'); + if (options.scope) createScope(); + flowContext.pushScope(true); + var body = parseBlock(flowContext); + flowContext.popScope(); + if (options.scope) destroyScope(); + expect('end'); + return finishNode(ast.whileStatement(condition, body)); + } + + // repeat ::= 'repeat' block 'until' exp + + function parseRepeatStatement(flowContext) { + if (options.scope) createScope(); + flowContext.pushScope(true); + var body = parseBlock(flowContext); + expect('until'); + flowContext.raiseDeferredErrors(); + var condition = parseExpectedExpression(flowContext); + flowContext.popScope(); + if (options.scope) destroyScope(); + return finishNode(ast.repeatStatement(condition, body)); + } + + // retstat ::= 'return' [exp {',' exp}] [';'] + + function parseReturnStatement(flowContext) { + var expressions = []; + + if ('end' !== token.value) { + var expression = parseExpression(flowContext); + if (null != expression) expressions.push(expression); + while (consume(',')) { + expression = parseExpectedExpression(flowContext); + expressions.push(expression); + } + consume(';'); // grammar tells us ; is optional here. + } + return finishNode(ast.returnStatement(expressions)); + } + + // if ::= 'if' exp 'then' block {elif} ['else' block] 'end' + // elif ::= 'elseif' exp 'then' block + + function parseIfStatement(flowContext) { + var clauses = [] + , condition + , body + , marker; + + // IfClauses begin at the same location as the parent IfStatement. + // It ends at the start of `end`, `else`, or `elseif`. + if (trackLocations) { + marker = locations[locations.length - 1]; + locations.push(marker); + } + condition = parseExpectedExpression(flowContext); + expect('then'); + if (options.scope) createScope(); + flowContext.pushScope(); + body = parseBlock(flowContext); + flowContext.popScope(); + if (options.scope) destroyScope(); + clauses.push(finishNode(ast.ifClause(condition, body))); + + if (trackLocations) marker = createLocationMarker(); + while (consume('elseif')) { + pushLocation(marker); + condition = parseExpectedExpression(flowContext); + expect('then'); + if (options.scope) createScope(); + flowContext.pushScope(); + body = parseBlock(flowContext); + flowContext.popScope(); + if (options.scope) destroyScope(); + clauses.push(finishNode(ast.elseifClause(condition, body))); + if (trackLocations) marker = createLocationMarker(); + } + + if (consume('else')) { + // Include the `else` in the location of ElseClause. + if (trackLocations) { + marker = new Marker(previousToken); + locations.push(marker); + } + if (options.scope) createScope(); + flowContext.pushScope(); + body = parseBlock(flowContext); + flowContext.popScope(); + if (options.scope) destroyScope(); + clauses.push(finishNode(ast.elseClause(body))); + } + + expect('end'); + return finishNode(ast.ifStatement(clauses)); + } + + // There are two types of for statements, generic and numeric. + // + // for ::= Name '=' exp ',' exp [',' exp] 'do' block 'end' + // for ::= namelist 'in' explist 'do' block 'end' + // namelist ::= Name {',' Name} + // explist ::= exp {',' exp} + + function parseForStatement(flowContext) { + var variable = parseIdentifier() + , body; + + // The start-identifier is local. + + if (options.scope) { + createScope(); + scopeIdentifier(variable); + } + + // If the first expression is followed by a `=` punctuator, this is a + // Numeric For Statement. + if (consume('=')) { + // Start expression + var start = parseExpectedExpression(flowContext); + expect(','); + // End expression + var end = parseExpectedExpression(flowContext); + // Optional step expression + var step = consume(',') ? parseExpectedExpression(flowContext) : null; + + expect('do'); + flowContext.pushScope(true); + body = parseBlock(flowContext); + flowContext.popScope(); + expect('end'); + if (options.scope) destroyScope(); + + return finishNode(ast.forNumericStatement(variable, start, end, step, body)); + } + // If not, it's a Generic For Statement + else { + // The namelist can contain one or more identifiers. + var variables = [variable]; + while (consume(',')) { + variable = parseIdentifier(); + // Each variable in the namelist is locally scoped. + if (options.scope) scopeIdentifier(variable); + variables.push(variable); + } + expect('in'); + var iterators = []; + + // One or more expressions in the explist. + do { + var expression = parseExpectedExpression(flowContext); + iterators.push(expression); + } while (consume(',')); + + expect('do'); + flowContext.pushScope(true); + body = parseBlock(flowContext); + flowContext.popScope(); + expect('end'); + if (options.scope) destroyScope(); + + return finishNode(ast.forGenericStatement(variables, iterators, body)); + } + } + + // Local statements can either be variable assignments or function + // definitions. If a function definition is found, it will be delegated to + // `parseFunctionDeclaration()` with the isLocal flag. + // + // This AST structure might change into a local assignment with a function + // child. + // + // local ::= 'local' 'function' Name funcdecl + // | 'local' Name {',' Name} ['=' exp {',' exp}] + + function parseLocalStatement(flowContext) { + var name + , declToken = previousToken; + + if (Identifier === token.type) { + var variables = [] + , init = []; + + do { + name = parseIdentifier(); + + variables.push(name); + flowContext.addLocal(name.name, declToken); + } while (consume(',')); + + if (consume('=')) { + do { + var expression = parseExpectedExpression(flowContext); + init.push(expression); + } while (consume(',')); + } + + // Declarations doesn't exist before the statement has been evaluated. + // Therefore assignments can't use their declarator. And the identifiers + // shouldn't be added to the scope until the statement is complete. + if (options.scope) { + for (var i = 0, l = variables.length; i < l; ++i) { + scopeIdentifier(variables[i]); + } + } + + return finishNode(ast.localStatement(variables, init)); + } + if (consume('function')) { + name = parseIdentifier(); + flowContext.addLocal(name.name, declToken); + + if (options.scope) { + scopeIdentifier(name); + createScope(); + } + + // MemberExpressions are not allowed in local function statements. + return parseFunctionDeclaration(name, true); + } else { + raiseUnexpectedToken('', token); + } + } + + // assignment ::= varlist '=' explist + // var ::= Name | prefixexp '[' exp ']' | prefixexp '.' Name + // varlist ::= var {',' var} + // explist ::= exp {',' exp} + // + // call ::= callexp + // callexp ::= prefixexp args | prefixexp ':' Name args + + function parseAssignmentOrCallStatement(flowContext) { + // Keep a reference to the previous token for better error messages in case + // of invalid statement + var previous = token + , marker, startMarker; + var lvalue, base, name; + + var targets = []; + + if (trackLocations) startMarker = createLocationMarker(); + + do { + if (trackLocations) marker = createLocationMarker(); + + if (Identifier === token.type) { + name = token.value; + base = parseIdentifier(); + // Set the parent scope. + if (options.scope) attachScope(base, scopeHasName(name)); + lvalue = true; + } else if ('(' === token.value) { + next(); + base = parseExpectedExpression(flowContext); + expect(')'); + lvalue = false; + } else { + return unexpected(token); + } + + both: for (;;) { + var newBase; + + switch (StringLiteral === token.type ? '"' : token.value) { + case '.': + case '[': + lvalue = true; + break; + case ':': + case '(': + case '{': + case '"': + lvalue = null; + break; + default: + break both; + } + + base = parsePrefixExpressionPart(base, marker, flowContext); + } + + targets.push(base); + + if (',' !== token.value) + break; + + if (!lvalue) { + return unexpected(token); + } + + next(); + } while (true); + + if (targets.length === 1 && lvalue === null) { + pushLocation(marker); + return finishNode(ast.callStatement(targets[0])); + } else if (!lvalue) { + return unexpected(token); + } + + expect('='); + + var values = []; + + do { + values.push(parseExpectedExpression(flowContext)); + } while (consume(',')); + + pushLocation(startMarker); + return finishNode(ast.assignmentStatement(targets, values)); + } + + // ### Non-statements + + // Identifier ::= Name + + function parseIdentifier() { + markLocation(); + var identifier = token.value; + if (Identifier !== token.type) raiseUnexpectedToken('', token); + next(); + return finishNode(ast.identifier(identifier)); + } + + function parseParameterList(flowContext, terminator) { + var parameters = []; + + // The declaration has arguments + if (!consume(terminator)) { + // Arguments are a comma separated list of identifiers, optionally ending + // with a vararg. + while (true) { + if (Identifier === token.type) { + var parameter = parseIdentifier(); + // Function parameters are local. + if (options.scope) scopeIdentifier(parameter); + + parameters.push(parameter); + + if (consume(',')) continue; + } + // No arguments are allowed after a vararg. + else if (VarargLiteral === token.type) { + flowContext.allowVararg = true; + parameters.push(parsePrimaryExpression(flowContext)); + } else { + raiseUnexpectedToken(' or \'...\'', token); + } + expect(terminator); + break; + } + } + + return parameters; + } + + // Parse the functions parameters and body block. The name should already + // have been parsed and passed to this declaration function. By separating + // this we allow for anonymous functions in expressions. + // + // For local functions there's a boolean parameter which needs to be set + // when parsing the declaration. + // + // funcdecl ::= '(' [parlist] ')' block 'end' + // parlist ::= Name {',' Name} | [',' '...'] | '...' + + function parseFunctionDeclaration(name, isLocal) { + var flowContext = makeFlowContext(); + flowContext.pushScope(); + + expect('('); + + var parameters = parseParameterList(flowContext, ')'); + var body = parseBlock(flowContext); + + flowContext.popScope(); + expect('end'); + if (options.scope) destroyScope(); + + isLocal = isLocal || false; + return finishNode(ast.functionStatement(name, parameters, isLocal, body)); + } + + // Parse a MoonSharp short anonymous function. Unlike regular function + // syntax which can appear as both a statement and an expression, short + // anonymous functions are only allowed as an expression. Short anonymous + // functions are made of a parameter list and a singular expression. + // + // shortfuncdef ::= '|' [parlist] '|' expression + // parlist ::= Name {',' Name} | [',' '...'] | '...' + + function parseShortFunctionDefinition() { + var flowContext = makeFlowContext(); + flowContext.pushScope(); + + var parameters = parseParameterList(flowContext, '|'); + + if (token.type === Punctuator && token.value === '|') { // Non-parenthesized nested short functions are forbidden. + raiseUnexpectedToken('', token); + } + + var expression = parseExpectedExpression(flowContext); + + flowContext.popScope(); + if (options.scope) destroyScope(); + + return finishNode(ast.shortFunctionDefinition(parameters, expression)); + } + + // Parse the function name as identifiers and member expressions. + // + // Name {'.' Name} [':' Name] + + function parseFunctionName() { + var base, name, marker; + + if (trackLocations) marker = createLocationMarker(); + base = parseIdentifier(); + + if (options.scope) { + attachScope(base, scopeHasName(base.name)); + createScope(); + } + + while (consume('.')) { + pushLocation(marker); + name = parseIdentifier(); + base = finishNode(ast.memberExpression(base, '.', name)); + } + + if (consume(':')) { + pushLocation(marker); + name = parseIdentifier(); + base = finishNode(ast.memberExpression(base, ':', name)); + if (options.scope) scopeIdentifierName('self'); + } + + return base; + } + + // tableconstructor ::= '{' [fieldlist] '}' + // fieldlist ::= field {fieldsep field} fieldsep + // field ::= '[' exp ']' '=' exp | Name = 'exp' | exp + // + // fieldsep ::= ',' | ';' + + function parseTableConstructor(flowContext) { + var fields = [] + , key, value; + + while (true) { + markLocation(); + if (Punctuator === token.type && consume('[')) { + key = parseExpectedExpression(flowContext); + expect(']'); + expect('='); + value = parseExpectedExpression(flowContext); + fields.push(finishNode(ast.tableKey(key, value))); + } else if (Identifier === token.type) { + if ('=' === lookahead.value) { + key = parseIdentifier(); + next(); + value = parseExpectedExpression(flowContext); + fields.push(finishNode(ast.tableKeyString(key, value))); + } else { + value = parseExpectedExpression(flowContext); + fields.push(finishNode(ast.tableValue(value))); + } + } else { + if (null == (value = parseExpression(flowContext))) { + locations.pop(); + break; + } + fields.push(finishNode(ast.tableValue(value))); + } + if (',;'.indexOf(token.value) >= 0) { + next(); + continue; + } + break; + } + expect('}'); + return finishNode(ast.tableConstructorExpression(fields)); + } + + // Expression parser + // ----------------- + // + // Expressions are evaluated and always return a value. If nothing is + // matched null will be returned. + // + // exp ::= (unop exp | primary | prefixexp ) { binop exp } + // + // primary ::= nil | false | true | Number | String | '...' + // | functiondef | shortfunctiondef | tableconstructor + // + // prefixexp ::= (Name | '(' exp ')' ) { '[' exp ']' + // | '.' Name | ':' Name args | args } + // + + function parseExpression(flowContext) { + var expression = parseSubExpression(0, flowContext); + return expression; + } + + // Parse an expression expecting it to be valid. + + function parseExpectedExpression(flowContext) { + var expression = parseExpression(flowContext); + if (null == expression) raiseUnexpectedToken('', token); + else return expression; + } + + + // Return the precedence priority of the operator. + // + // As unary `-` can't be distinguished from binary `-`, unary precedence + // isn't described in this table but in `parseSubExpression()` itself. + // + // As this function gets hit on every expression it's been optimized due to + // the expensive CompareICStub which took ~8% of the parse time. + + function binaryPrecedence(operator) { + var charCode = operator.charCodeAt(0) + , length = operator.length; + + if (1 === length) { + switch (charCode) { + case 94: return 12; // ^ + case 42: case 47: case 37: return 10; // * / % + case 43: case 45: return 9; // + - + case 38: return 6; // & + case 126: return 5; // ~ + case 124: return 4; // | + case 60: case 62: return 3; // < > + } + } else if (2 === length) { + switch (charCode) { + case 47: return 10; // // + case 46: return 8; // .. + case 60: case 62: + if('<<' === operator || '>>' === operator) return 7; // << >> + return 3; // <= >= + case 61: case 126: case 33: return 3; // == ~= != + case 111: return 1; // or + } + } else if (97 === charCode && 'and' === operator) return 2; + return 0; + } + + // Implement an operator-precedence parser to handle binary operator + // precedence. + // + // We use this algorithm because it's compact, it's fast and Lua core uses + // the same so we can be sure our expressions are parsed in the same manner + // without excessive amounts of tests. + // + // exp ::= (unop exp | primary | prefixexp ) { binop exp } + + function parseSubExpression(minPrecedence, flowContext) { + var operator = token.value + // The left-hand side in binary operations. + , expression, marker; + + if (trackLocations) marker = createLocationMarker(); + + // UnaryExpression + if (isUnary(token)) { + markLocation(); + next(); + var argument = parseSubExpression(10, flowContext); + if (argument == null) raiseUnexpectedToken('', token); + expression = finishNode(ast.unaryExpression(operator, argument)); + } + if (null == expression) { + // PrimaryExpression + expression = parsePrimaryExpression(flowContext); + + // PrefixExpression + if (null == expression) { + expression = parsePrefixExpression(flowContext); + } + } + // This is not a valid left hand expression. + if (null == expression) return null; + + var precedence; + while (true) { + operator = token.value; + + precedence = (Punctuator === token.type || Keyword === token.type) ? + binaryPrecedence(operator) : 0; + + if (precedence === 0 || precedence <= minPrecedence) break; + // Right-hand precedence operators + if ('^' === operator || '..' === operator) --precedence; + next(); + var right = parseSubExpression(precedence, flowContext); + if (null == right) raiseUnexpectedToken('', token); + // Push in the marker created before the loop to wrap its entirety. + if (trackLocations) locations.push(marker); + expression = finishNode(ast.binaryExpression(operator, expression, right)); + + } + return expression; + } + + // prefixexp ::= prefix {suffix} + // prefix ::= Name | '(' exp ')' + // suffix ::= '[' exp ']' | '.' Name | ':' Name args | args + // + // args ::= '(' [explist] ')' | tableconstructor | String + + function parsePrefixExpressionPart(base, marker, flowContext) { + var expressions, identifier; + + if (Punctuator === token.type) { + switch (token.value) { + case '[': + pushLocation(marker); + next(); + expressions = []; + expressions.push(parseExpectedExpression(flowContext)); + while (consume(',')) { + expressions.push(parseExpectedExpression(flowContext)); + } + expect(']'); + return expressions.length === 1 ? finishNode(ast.indexExpression(base, expressions[0])) + : finishNode(ast.indexesExpression(base, expressions)); + case '.': + pushLocation(marker); + next(); + identifier = parseIdentifier(); + return finishNode(ast.memberExpression(base, '.', identifier)); + case ':': + pushLocation(marker); + next(); + identifier = parseIdentifier(); + base = finishNode(ast.memberExpression(base, ':', identifier)); + // Once a : is found, this has to be a CallExpression, otherwise + // throw an error. + pushLocation(marker); + return parseCallExpression(base, flowContext); + case '(': case '{': // args + pushLocation(marker); + return parseCallExpression(base, flowContext); + } + } else if (StringLiteral === token.type) { + pushLocation(marker); + return parseCallExpression(base, flowContext); + } + + return null; + } + + function parsePrefixExpression(flowContext) { + var base, name, marker; + + if (trackLocations) marker = createLocationMarker(); + + // The prefix + if (Identifier === token.type) { + name = token.value; + base = parseIdentifier(); + // Set the parent scope. + if (options.scope) attachScope(base, scopeHasName(name)); + } else if (consume('(')) { + base = parseExpectedExpression(flowContext); + expect(')'); + } else { + return null; + } + + // The suffix + for (;;) { + var newBase = parsePrefixExpressionPart(base, marker, flowContext); + if (newBase === null) + break; + base = newBase; + } + + return base; + } + + // args ::= '(' [explist] ')' | tableconstructor | String + + function parseCallExpression(base, flowContext) { + if (Punctuator === token.type) { + switch (token.value) { + case '(': + if (!features.emptyStatement) { + if (token.line !== previousToken.line) + raise(null, errors.ambiguousSyntax, token.value); + } + next(); + + // List of expressions + var expressions = []; + var expression = parseExpression(flowContext); + if (null != expression) expressions.push(expression); + while (consume(',')) { + expression = parseExpectedExpression(flowContext); + expressions.push(expression); + } + + expect(')'); + return finishNode(ast.callExpression(base, expressions)); + + case '{': + markLocation(); + next(); + var table = parseTableConstructor(flowContext); + return finishNode(ast.tableCallExpression(base, table)); + } + } else if (StringLiteral === token.type) { + return finishNode(ast.stringCallExpression(base, parsePrimaryExpression(flowContext))); + } + + raiseUnexpectedToken('function arguments', token); + } + + // primary ::= String | Numeric | nil | true | false + // | functiondef | shortfunctiondef | tableconstructor | '...' + + function parsePrimaryExpression(flowContext) { + var literals = StringLiteral | NumericLiteral | BooleanLiteral | NilLiteral | VarargLiteral + , value = token.value + , type = token.type + , marker; + + if (trackLocations) marker = createLocationMarker(); + + if (type === VarargLiteral && !flowContext.allowVararg) { + raise(token, errors.cannotUseVararg, token.value); + } + + if (type & literals) { + pushLocation(marker); + var raw = input.slice(token.range[0], token.range[1]); + next(); + return finishNode(ast.literal(type, value, raw)); + } else if ((Keyword === type && 'function' === value) || (Punctuator === type && '|' === value)) { + pushLocation(marker); + next(); + if (options.scope) createScope(); + return Punctuator === type ? parseShortFunctionDefinition() : parseFunctionDeclaration(null); + } else if (consume('{')) { + pushLocation(marker); + return parseTableConstructor(flowContext); + } + } + + // Parser + // ------ + + // Export the main parser. + // + // - `wait` Hold parsing until end() is called. Defaults to false + // - `comments` Store comments. Defaults to true. + // - `scope` Track identifier scope. Defaults to false. + // - `locations` Store location information. Defaults to false. + // - `ranges` Store the start and end character locations. Defaults to + // false. + // - `onCreateNode` Callback which will be invoked when a syntax node is + // created. + // - `onCreateScope` Callback which will be invoked when a new scope is + // created. + // - `onDestroyScope` Callback which will be invoked when the current scope + // is destroyed. + // + // Example: + // + // var parser = require('luaparser'); + // parser.parse('i = 0'); + + exports.parse = parse; + + var versionFeatures = { + '5.1': { + }, + '5.2': { + labels: true, + emptyStatement: true, + hexEscapes: true, + skipWhitespaceEscape: true, + strictEscapes: true, + relaxedBreak: true + }, + '5.3': { + labels: true, + emptyStatement: true, + hexEscapes: true, + skipWhitespaceEscape: true, + strictEscapes: true, + unicodeEscapes: true, + bitwiseOperators: true, + integerDivision: true, + relaxedBreak: true + }, + 'LuaJIT': { + // XXX: LuaJIT language features may depend on compilation options; may need to + // rethink how to handle this. Specifically, there is a LUAJIT_ENABLE_LUA52COMPAT + // that removes contextual goto. Maybe add 'LuaJIT-5.2compat' as well? + labels: true, + contextualGoto: true, + hexEscapes: true, + skipWhitespaceEscape: true, + strictEscapes: true, + unicodeEscapes: true + } + }; + + function parse(_input, _options) { + if ('undefined' === typeof _options && 'object' === typeof _input) { + _options = _input; + _input = undefined; + } + if (!_options) _options = {}; + + input = _input || ''; + options = assign({}, defaultOptions, _options); + + // Rewind the lexer + index = 0; + line = 1; + lineStart = 0; + length = input.length; + // When tracking identifier scope, initialize with an empty scope. + scopes = [[]]; + scopeDepth = 0; + globals = []; + locations = []; + + if (!Object.prototype.hasOwnProperty.call(versionFeatures, options.luaVersion)) { + throw new Error(sprintf("Lua version '%1' not supported", options.luaVersion)); + } + + features = assign({}, versionFeatures[options.luaVersion]); + if (options.extendedIdentifiers !== void 0) + features.extendedIdentifiers = !!options.extendedIdentifiers; + + if (options.comments) comments = []; + if (!options.wait) return end(); + return exports; + } + + // Write to the source code buffer without beginning the parse. + exports.write = write; + + function write(_input) { + input += String(_input); + length = input.length; + return exports; + } + + // Send an EOF and begin parsing. + exports.end = end; + + function end(_input) { + if ('undefined' !== typeof _input) write(_input); + + // Ignore shebangs. + if (input && input.substr(0, 2) === '#!') input = input.replace(/^.*/, function (line) { + return line.replace(/./g, ' '); + }); + + length = input.length; + trackLocations = options.locations || options.ranges; + // Initialize with a lookahead token. + lookahead = lex(); + + var chunk = parseChunk(); + if (options.comments) chunk.comments = comments; + if (options.scope) chunk.globals = globals; + + /* istanbul ignore if */ + if (locations.length > 0) + throw new Error('Location tracking failed. This is most likely a bug in luaparse'); + + return chunk; + } + +})); +/* vim: set sw=2 ts=2 et tw=79 : */ /***/ }), -/***/ 412: +/***/ 7858: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -3514,7 +10459,53 @@ exports.fromPromise = function (fn) { /***/ }), -/***/ 357: +/***/ 8891: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const path_1 = __importDefault(__nccwpck_require__(1017)); +const core_1 = __nccwpck_require__(3539); +const fs_extra_1 = __importDefault(__nccwpck_require__(6417)); +const luabundle_1 = __importDefault(__nccwpck_require__(1054)); +const luamin_1 = __importDefault(__nccwpck_require__(4825)); +const IS_DEV_ENVIRONMENT = process.env.NODE_ENV === 'development'; +const sourcePath = IS_DEV_ENVIRONMENT + ? path_1.default.join('..', '..', '..', 'src') + : path_1.default.join(...(0, core_1.getInput)('source', { required: true }).split('/')); +const outputPath = IS_DEV_ENVIRONMENT + ? path_1.default.join('..', '..', '..', 'dist') + : path_1.default.join(...(0, core_1.getInput)('output', { required: true }).split('/')); +/* + remove old bundled files (if they exist) + */ +fs_extra_1.default.ensureDirSync(outputPath); +fs_extra_1.default.readdirSync(outputPath).forEach(fileName => fs_extra_1.default.removeSync(fileName)); +/* + bundle and save source files + */ +const sourceFiles = fs_extra_1.default.readdirSync(sourcePath).filter(fileName => fileName.endsWith('.lua')); +const mixins = fs_extra_1.default.readdirSync(path_1.default.join(sourcePath, 'mixin')).filter(fileName => fileName.endsWith('.lua')); +sourceFiles.forEach(file => { + if (file.startsWith('personal')) + return; + const source = fs_extra_1.default.readFileSync(path_1.default.join(sourcePath, file), 'utf8'); + let expressionHandlerOutput = source.includes('library.mixin') ? mixins : []; + const bundledFile = luabundle_1.default.bundle(path_1.default.join(sourcePath, file), { + paths: ['?', '?.lua', path_1.default.resolve(path_1.default.join(sourcePath, 'library', 'general_library.lua'))], + expressionHandler: (_, __) => expressionHandlerOutput, + }); + fs_extra_1.default.writeFileSync(path_1.default.join(outputPath, file), luamin_1.default.minify(bundledFile)); +}); + + +/***/ }), + +/***/ 9491: /***/ ((module) => { "use strict"; @@ -3522,7 +10513,7 @@ module.exports = require("assert"); /***/ }), -/***/ 619: +/***/ 2057: /***/ ((module) => { "use strict"; @@ -3530,7 +10521,7 @@ module.exports = require("constants"); /***/ }), -/***/ 747: +/***/ 7147: /***/ ((module) => { "use strict"; @@ -3538,7 +10529,7 @@ module.exports = require("fs"); /***/ }), -/***/ 87: +/***/ 2037: /***/ ((module) => { "use strict"; @@ -3546,7 +10537,7 @@ module.exports = require("os"); /***/ }), -/***/ 622: +/***/ 1017: /***/ ((module) => { "use strict"; @@ -3554,7 +10545,7 @@ module.exports = require("path"); /***/ }), -/***/ 413: +/***/ 2781: /***/ ((module) => { "use strict"; @@ -3562,12 +10553,20 @@ module.exports = require("stream"); /***/ }), -/***/ 669: +/***/ 3837: /***/ ((module) => { "use strict"; module.exports = require("util"); +/***/ }), + +/***/ 4131: +/***/ ((module) => { + +"use strict"; +module.exports = JSON.parse('{"name":"luabundle","description":"Bundles several Lua files into a single file.","version":"1.6.0","author":"Benjamin Dobell @Benjamin-Dobell","bugs":"https://github.com/Benjamin-Dobell/luabundle/issues","dependencies":{"moonsharp-luaparse":"^0.2.4","tslib":"^1"},"devDependencies":{"@types/node":"^13.7.2","eslint":"^6.8.0","fs-extra":"^8.1.0","klaw-sync":"^6.0.0","rimraf":"^3.0.2","typescript":"^3.7.5"},"engines":{"node":">=8.0.0"},"homepage":"https://github.com/Benjamin-Dobell/luabundle","keywords":["lua","require","modules","bundler","bundling"],"license":"MIT","main":"index.js","repository":"Benjamin-Dobell/luabundle","scripts":{"build":"node scripts/build.js","prepack":"node scripts/prepack.js","posttest":"eslint . --config .eslintrc","test":"echo NO TESTS"},"types":"index.d.ts"}'); + /***/ }) /******/ }); @@ -3584,8 +10583,8 @@ module.exports = require("util"); /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed +/******/ id: moduleId, +/******/ loaded: false, /******/ exports: {} /******/ }; /******/ @@ -3598,48 +10597,20 @@ module.exports = require("util"); /******/ if(threw) delete __webpack_module_cache__[moduleId]; /******/ } /******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ -/******/ /* webpack/runtime/compat get default export */ -/******/ (() => { -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __nccwpck_require__.n = (module) => { -/******/ var getter = module && module.__esModule ? -/******/ () => (module['default']) : -/******/ () => (module); -/******/ __nccwpck_require__.d(getter, { a: getter }); -/******/ return getter; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/define property getters */ +/******/ /* webpack/runtime/node module decorator */ /******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __nccwpck_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __nccwpck_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ __nccwpck_require__.nmd = (module) => { +/******/ module.paths = []; +/******/ if (!module.children) module.children = []; +/******/ return module; /******/ }; /******/ })(); /******/ @@ -3648,156 +10619,12 @@ module.exports = require("util"); /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; /******/ /************************************************************************/ -var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be in strict mode. -(() => { -"use strict"; -// ESM COMPAT FLAG -__nccwpck_require__.r(__webpack_exports__); - -// EXTERNAL MODULE: external "path" -var external_path_ = __nccwpck_require__(622); -var external_path_default = /*#__PURE__*/__nccwpck_require__.n(external_path_); -// EXTERNAL MODULE: ./node_modules/.pnpm/@actions+core@1.4.0/node_modules/@actions/core/lib/core.js -var core = __nccwpck_require__(283); -// EXTERNAL MODULE: ./node_modules/.pnpm/fs-extra@10.0.0/node_modules/fs-extra/lib/index.js -var lib = __nccwpck_require__(372); -var lib_default = /*#__PURE__*/__nccwpck_require__.n(lib); -;// CONCATENATED MODULE: ./src/helpers.ts -const getImport = (line) => { - const matches = line.match(/require\(["']library\.([A-Z_a-z]+)["']\)/iu); - if (!matches) - return { importedFile: '', isImport: false }; - return { importedFile: matches[1], isImport: true }; -}; -const getVariableName = (line) => { - const matches = line.match(/^(local )?([a-zA-Z_]+)/u); - if (!matches) - return ''; - return matches[2]; -}; -const getAllImports = (file) => { - const imports = new Set(); - const lines = file.split('\n'); - for (const line of lines) { - const { isImport, importedFile } = getImport(line); - if (isImport) - imports.add(importedFile); - } - return imports; -}; - -;// CONCATENATED MODULE: ./src/bundle.ts - -const bundleFile = (file, library) => { - const lines = file.split('\n'); - const output = []; - lines.forEach((line) => { - const { importedFile, isImport } = getImport(line); - if (!isImport && !(importedFile in library)) { - output.push(line); - return; - } - const variableName = getVariableName(line); - output.push(library[importedFile].contents.replace(/BUNDLED_LIBRARY_VARIABLE_NAME/gu, variableName)); - }); - return output.join('\n'); -}; - -;// CONCATENATED MODULE: ./src/prepare-library.ts - - -const LIBRARY_VARIABLE_NAME = 'BUNDLED_LIBRARY_VARIABLE_NAME'; -const prepareLibraryFile = (file, fileName) => { - const lines = file.split('\n'); - let internalModuleName = fileName; - if (!internalModuleName.match(new RegExp(`local ${internalModuleName} = \\{\\}`, 'u'))) { - for (const line of lines) { - const matches = line.match(/^local ([a-zA-Z+]+) = \{\}/iu); - if (matches === null || matches === void 0 ? void 0 : matches[1]) { - internalModuleName = matches[1]; - break; - } - } - } - const output = []; - const declarationRegex = new RegExp(`^local ${internalModuleName} = \\{\\}`, 'u'); - const methodRegex = new RegExp(`^function ${internalModuleName}\\.`, 'ug'); - const returnRegex = new RegExp(`^return ${internalModuleName}`, 'ug'); - for (const line of lines) { - let outputtedLine = line.replace(declarationRegex, `local ${LIBRARY_VARIABLE_NAME} = {}`); - outputtedLine = outputtedLine.replace(methodRegex, `function ${LIBRARY_VARIABLE_NAME}.`); - outputtedLine = outputtedLine.replace(returnRegex, ``); - output.push(outputtedLine); - } - return { contents: output.join('\n') }; -}; -/* eslint-disable-next-line sonarjs/cognitive-complexity -- fix in the future */ -const prepareLibrary = (inputFiles) => { - let files = [...inputFiles]; - const library = {}; - let counter = 0; - while (files.length > 0 && counter < 10) { - const completedFiles = new Set(); - for (const file of files) { - const imports = getAllImports(file.contents); - for (const importFile of imports) - if (importFile in library) - imports.delete(importFile); - /* eslint-disable-next-line no-continue -- it's just best here */ - if (imports.size > 0) - continue; - const preparedFile = prepareLibraryFile(file.contents, file.fileName); - const bundledFile = bundleFile(preparedFile.contents, library); - completedFiles.add(file.fileName); - library[file.fileName] = { contents: bundledFile }; - } - files = files.filter((file) => !completedFiles.has(file.fileName)); - counter++; - } - return library; -}; - -;// CONCATENATED MODULE: ./src/index.ts - - - - - -const sourcePath = external_path_default().join(...(0,core.getInput)('source', { required: true }).split('/')); -const libraryPath = external_path_default().join(sourcePath, 'library'); -const outputPath = external_path_default().join(...(0,core.getInput)('output', { required: true }).split('/')); -/* - create bundled library files - */ -const libraryFileNames = lib_default().readdirSync(libraryPath); -const libraryRawFiles = []; -libraryFileNames.forEach((fileName) => { - const name = fileName.replace('.lua', ''); - const contents = lib_default().readFileSync(external_path_default().join(libraryPath, fileName)).toString(); - libraryRawFiles.push({ - fileName: name, - contents, - }); -}); -const library = prepareLibrary(libraryRawFiles); -/* - remove old bundled files (if they exist) - */ -lib_default().ensureDirSync(outputPath); -lib_default().readdirSync(outputPath).forEach((fileName) => lib_default().removeSync(fileName)); -/* - bundle and save source files - */ -const sourceFiles = lib_default().readdirSync(sourcePath).filter((fileName) => fileName.endsWith('.lua')); -sourceFiles.forEach((file) => { - const contents = lib_default().readFileSync(external_path_default().join(sourcePath, file)).toString(); - const bundledFile = bundleFile(contents, library); - lib_default().writeFileSync(external_path_default().join(outputPath, file), bundledFile); -}); - -})(); - -module.exports = __webpack_exports__; +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module is referenced by other modules so it can't be inlined +/******/ var __webpack_exports__ = __nccwpck_require__(8891); +/******/ module.exports = __webpack_exports__; +/******/ /******/ })() ; \ No newline at end of file diff --git a/.github/actions/bundle/dist/runtime.lua b/.github/actions/bundle/dist/runtime.lua new file mode 100644 index 00000000..b1842c0b --- /dev/null +++ b/.github/actions/bundle/dist/runtime.lua @@ -0,0 +1,42 @@ +(function(superRequire) + local loadingPlaceholder = {[{}] = true} + + local register + local modules = {} + + local require + local loaded = {} + + register = function(name, body) + if not modules[name] then + modules[name] = body + end + end + + require = function(name) + local loadedModule = loaded[name] + + if loadedModule then + if loadedModule == loadingPlaceholder then + return nil + end + else + if not modules[name] then + if not superRequire then + local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name) + error('Tried to require ' .. identifier .. ', but no such module has been registered') + else + return superRequire(name) + end + end + + loaded[name] = loadingPlaceholder + loadedModule = modules[name](require, loaded, register, modules) + loaded[name] = loadedModule + end + + return loadedModule + end + + return require, loaded, register, modules +end) \ No newline at end of file diff --git a/.github/actions/bundle/package.json b/.github/actions/bundle/package.json index af68e697..fb829091 100644 --- a/.github/actions/bundle/package.json +++ b/.github/actions/bundle/package.json @@ -10,7 +10,7 @@ "test": "jest", "test:watch": "npm run test -- --watch", "build": "ncc build src/index.ts -o dist --target=es2015", - "dev": "npm run build && node dist/index.js" + "dev": "npm run build && NODE_ENV=development node dist/index.js" }, "jest": { "collectCoverage": true, @@ -32,17 +32,19 @@ "@types/jest": "^26.0.24", "@types/n-readlines": "^1.0.2", "@types/node": "^16.4.3", - "@vercel/ncc": "^0.29.0", - "esbuild": "^0.12.15", + "@vercel/ncc": "^0.34.0", + "esbuild": "^0.14.49", "esbuild-jest": "^0.5.0", "eslint": "^7.31.0", - "jest": "^27.0.6", + "jest": "^28.1.2", "prettier": "^2.3.2", - "typescript": "^4.3.5" + "typescript": "^4.7.4" }, "dependencies": { "@actions/core": "^1.4.0", "fs-extra": "^10.0.0", + "luabundle": "^1.6.0", + "luamin": "^1.0.4", "n-readlines": "^1.0.1" } } diff --git a/.github/actions/bundle/pnpm-lock.yaml b/.github/actions/bundle/pnpm-lock.yaml index 8da8d1a5..ae8ee5c2 100644 --- a/.github/actions/bundle/pnpm-lock.yaml +++ b/.github/actions/bundle/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: 5.3 +lockfileVersion: 5.4 specifiers: '@actions/core': ^1.4.0 @@ -7,34 +7,38 @@ specifiers: '@types/jest': ^26.0.24 '@types/n-readlines': ^1.0.2 '@types/node': ^16.4.3 - '@vercel/ncc': ^0.29.0 - esbuild: ^0.12.15 + '@vercel/ncc': ^0.34.0 + esbuild: ^0.14.49 esbuild-jest: ^0.5.0 eslint: ^7.31.0 fs-extra: ^10.0.0 - jest: ^27.0.6 + jest: ^28.1.2 + luabundle: ^1.6.0 + luamin: ^1.0.4 n-readlines: ^1.0.1 prettier: ^2.3.2 - typescript: ^4.3.5 + typescript: ^4.7.4 dependencies: '@actions/core': 1.4.0 fs-extra: 10.0.0 + luabundle: 1.6.0 + luamin: 1.0.4 n-readlines: 1.0.1 devDependencies: - '@nick-mazuk/eslint-config': 0.8.13_eslint@7.31.0+typescript@4.3.5 + '@nick-mazuk/eslint-config': 0.8.13_6r4pv37zydmmxnib4wi3jja2me '@types/fs-extra': 9.0.12 '@types/jest': 26.0.24 '@types/n-readlines': 1.0.2 '@types/node': 16.4.3 - '@vercel/ncc': 0.29.0 - esbuild: 0.12.15 - esbuild-jest: 0.5.0_esbuild@0.12.15 + '@vercel/ncc': 0.34.0 + esbuild: 0.14.49 + esbuild-jest: 0.5.0_esbuild@0.14.49 eslint: 7.31.0 - jest: 27.0.6 + jest: 28.1.2_@types+node@16.4.3 prettier: 2.3.2 - typescript: 4.3.5 + typescript: 4.7.4 packages: @@ -83,7 +87,7 @@ packages: - supports-color dev: true - /@babel/eslint-parser/7.14.7_@babel+core@7.14.8+eslint@7.31.0: + /@babel/eslint-parser/7.14.7_xplm2s2mesxy5ln3rqv4brcbla: resolution: {integrity: sha512-6WPwZqO5priAGIwV6msJcdc9TsEPzYeYdS/Xuoap+/ihkgN6dzHp2bcAAwyWZ5bLzk0vvjDmKvRwkqNaiJ8BiQ==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: @@ -244,6 +248,8 @@ packages: resolution: {integrity: sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==} engines: {node: '>=6.0.0'} hasBin: true + dependencies: + '@babel/types': 7.14.8 dev: true /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.14.8: @@ -490,98 +496,114 @@ packages: engines: {node: '>=8'} dev: true - /@jest/console/27.0.6: - resolution: {integrity: sha512-fMlIBocSHPZ3JxgWiDNW/KPj6s+YRd0hicb33IrmelCcjXo/pXPwvuiKFmZz+XuqI/1u7nbUK10zSsWL/1aegg==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/console/28.1.1: + resolution: {integrity: sha512-0RiUocPVFEm3WRMOStIHbRWllG6iW6E3/gUPnf4lkrVFyXIIDeCe+vlKeYyFOMhB2EPE6FLFCNADSOOQMaqvyA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 + '@jest/types': 28.1.1 '@types/node': 16.4.3 chalk: 4.1.1 - jest-message-util: 27.0.6 - jest-util: 27.0.6 + jest-message-util: 28.1.1 + jest-util: 28.1.1 slash: 3.0.0 dev: true - /@jest/core/27.0.6: - resolution: {integrity: sha512-SsYBm3yhqOn5ZLJCtccaBcvD/ccTLCeuDv8U41WJH/V1MW5eKUkeMHT9U+Pw/v1m1AIWlnIW/eM2XzQr0rEmow==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/core/28.1.2: + resolution: {integrity: sha512-Xo4E+Sb/nZODMGOPt2G3cMmCBqL4/W2Ijwr7/mrXlq4jdJwcFQ/9KrrJZT2adQRk2otVBXXOz1GRQ4Z5iOgvRQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: node-notifier: optional: true dependencies: - '@jest/console': 27.0.6 - '@jest/reporters': 27.0.6 - '@jest/test-result': 27.0.6 - '@jest/transform': 27.0.6 - '@jest/types': 27.0.6 + '@jest/console': 28.1.1 + '@jest/reporters': 28.1.2 + '@jest/test-result': 28.1.1 + '@jest/transform': 28.1.2 + '@jest/types': 28.1.1 '@types/node': 16.4.3 ansi-escapes: 4.3.2 chalk: 4.1.1 - emittery: 0.8.1 + ci-info: 3.2.0 exit: 0.1.2 - graceful-fs: 4.2.6 - jest-changed-files: 27.0.6 - jest-config: 27.0.6 - jest-haste-map: 27.0.6 - jest-message-util: 27.0.6 - jest-regex-util: 27.0.6 - jest-resolve: 27.0.6 - jest-resolve-dependencies: 27.0.6 - jest-runner: 27.0.6 - jest-runtime: 27.0.6 - jest-snapshot: 27.0.6 - jest-util: 27.0.6 - jest-validate: 27.0.6 - jest-watcher: 27.0.6 + graceful-fs: 4.2.10 + jest-changed-files: 28.0.2 + jest-config: 28.1.2_@types+node@16.4.3 + jest-haste-map: 28.1.1 + jest-message-util: 28.1.1 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.1 + jest-resolve-dependencies: 28.1.2 + jest-runner: 28.1.2 + jest-runtime: 28.1.2 + jest-snapshot: 28.1.2 + jest-util: 28.1.1 + jest-validate: 28.1.1 + jest-watcher: 28.1.1 micromatch: 4.0.4 - p-each-series: 2.2.0 + pretty-format: 28.1.1 rimraf: 3.0.2 slash: 3.0.0 strip-ansi: 6.0.0 transitivePeerDependencies: - - bufferutil - - canvas - supports-color - ts-node - - utf-8-validate dev: true - /@jest/environment/27.0.6: - resolution: {integrity: sha512-4XywtdhwZwCpPJ/qfAkqExRsERW+UaoSRStSHCCiQTUpoYdLukj+YJbQSFrZjhlUDRZeNiU9SFH0u7iNimdiIg==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/environment/28.1.2: + resolution: {integrity: sha512-I0CR1RUMmOzd0tRpz10oUfaChBWs+/Hrvn5xYhMEF/ZqrDaaeHwS8yDBqEWCrEnkH2g+WE/6g90oBv3nKpcm8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/fake-timers': 27.0.6 - '@jest/types': 27.0.6 + '@jest/fake-timers': 28.1.2 + '@jest/types': 28.1.1 '@types/node': 16.4.3 - jest-mock: 27.0.6 + jest-mock: 28.1.1 dev: true - /@jest/fake-timers/27.0.6: - resolution: {integrity: sha512-sqd+xTWtZ94l3yWDKnRTdvTeZ+A/V7SSKrxsrOKSqdyddb9CeNRF8fbhAU0D7ZJBpTTW2nbp6MftmKJDZfW2LQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/expect-utils/28.1.1: + resolution: {integrity: sha512-n/ghlvdhCdMI/hTcnn4qV57kQuV9OTsZzH1TTCVARANKhl6hXJqLKUkwX69ftMGpsbpt96SsDD8n8LD2d9+FRw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 - '@sinonjs/fake-timers': 7.1.2 + jest-get-type: 28.0.2 + dev: true + + /@jest/expect/28.1.2: + resolution: {integrity: sha512-HBzyZBeFBiOelNbBKN0pilWbbrGvwDUwAqMC46NVJmWm8AVkuE58NbG1s7DR4cxFt4U5cVLxofAoHxgvC5MyOw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + expect: 28.1.1 + jest-snapshot: 28.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers/28.1.2: + resolution: {integrity: sha512-xSYEI7Y0D5FbZN2LsCUj/EKRR1zfQYmGuAUVh6xTqhx7V5JhjgMcK5Pa0iR6WIk0GXiHDe0Ke4A+yERKE9saqg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/types': 28.1.1 + '@sinonjs/fake-timers': 9.1.2 '@types/node': 16.4.3 - jest-message-util: 27.0.6 - jest-mock: 27.0.6 - jest-util: 27.0.6 + jest-message-util: 28.1.1 + jest-mock: 28.1.1 + jest-util: 28.1.1 dev: true - /@jest/globals/27.0.6: - resolution: {integrity: sha512-DdTGCP606rh9bjkdQ7VvChV18iS7q0IMJVP1piwTWyWskol4iqcVwthZmoJEf7obE1nc34OpIyoVGPeqLC+ryw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/globals/28.1.2: + resolution: {integrity: sha512-cz0lkJVDOtDaYhvT3Fv2U1B6FtBnV+OpEyJCzTHM1fdoTsU4QNLAt/H4RkiwEUU+dL4g/MFsoTuHeT2pvbo4Hg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/environment': 27.0.6 - '@jest/types': 27.0.6 - expect: 27.0.6 + '@jest/environment': 28.1.2 + '@jest/expect': 28.1.2 + '@jest/types': 28.1.1 + transitivePeerDependencies: + - supports-color dev: true - /@jest/reporters/27.0.6: - resolution: {integrity: sha512-TIkBt09Cb2gptji3yJXb3EE+eVltW6BjO7frO7NEfjI9vSIYoISi5R3aI3KpEDXlB1xwB+97NXIqz84qYeYsfA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/reporters/28.1.2: + resolution: {integrity: sha512-/whGLhiwAqeCTmQEouSigUZJPVl7sW8V26EiboImL+UyXznnr1a03/YZ2BX8OlFw0n+Zlwu+EZAITZtaeRTxyA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -589,62 +611,68 @@ packages: optional: true dependencies: '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 27.0.6 - '@jest/test-result': 27.0.6 - '@jest/transform': 27.0.6 - '@jest/types': 27.0.6 + '@jest/console': 28.1.1 + '@jest/test-result': 28.1.1 + '@jest/transform': 28.1.2 + '@jest/types': 28.1.1 + '@jridgewell/trace-mapping': 0.3.14 + '@types/node': 16.4.3 chalk: 4.1.1 collect-v8-coverage: 1.0.1 exit: 0.1.2 glob: 7.1.7 - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 istanbul-lib-coverage: 3.0.0 - istanbul-lib-instrument: 4.0.3 + istanbul-lib-instrument: 5.2.0 istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.0 - istanbul-reports: 3.0.2 - jest-haste-map: 27.0.6 - jest-resolve: 27.0.6 - jest-util: 27.0.6 - jest-worker: 27.0.6 + istanbul-reports: 3.1.4 + jest-message-util: 28.1.1 + jest-util: 28.1.1 + jest-worker: 28.1.1 slash: 3.0.0 - source-map: 0.6.1 string-length: 4.0.2 + strip-ansi: 6.0.0 terminal-link: 2.1.1 - v8-to-istanbul: 8.0.0 + v8-to-istanbul: 9.0.1 transitivePeerDependencies: - supports-color dev: true - /@jest/source-map/27.0.6: - resolution: {integrity: sha512-Fek4mi5KQrqmlY07T23JRi0e7Z9bXTOOD86V/uS0EIW4PClvPDqZOyFlLpNJheS6QI0FNX1CgmPjtJ4EA/2M+g==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/schemas/28.0.2: + resolution: {integrity: sha512-YVDJZjd4izeTDkij00vHHAymNXQ6WWsdChFRK86qck6Jpr3DCL5W3Is3vslviRlP+bLuMYRLbdp98amMvqudhA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@sinclair/typebox': 0.23.5 + dev: true + + /@jest/source-map/28.1.2: + resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: + '@jridgewell/trace-mapping': 0.3.14 callsites: 3.1.0 - graceful-fs: 4.2.6 - source-map: 0.6.1 + graceful-fs: 4.2.10 dev: true - /@jest/test-result/27.0.6: - resolution: {integrity: sha512-ja/pBOMTufjX4JLEauLxE3LQBPaI2YjGFtXexRAjt1I/MbfNlMx0sytSX3tn5hSLzQsR3Qy2rd0hc1BWojtj9w==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/test-result/28.1.1: + resolution: {integrity: sha512-hPmkugBktqL6rRzwWAtp1JtYT4VHwv8OQ+9lE5Gymj6dHzubI/oJHMUpPOt8NrdVWSrz9S7bHjJUmv2ggFoUNQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/console': 27.0.6 - '@jest/types': 27.0.6 + '@jest/console': 28.1.1 + '@jest/types': 28.1.1 '@types/istanbul-lib-coverage': 2.0.3 collect-v8-coverage: 1.0.1 dev: true - /@jest/test-sequencer/27.0.6: - resolution: {integrity: sha512-bISzNIApazYOlTHDum9PwW22NOyDa6VI31n6JucpjTVM0jD6JDgqEZ9+yn575nDdPF0+4csYDxNNW13NvFQGZA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/test-sequencer/28.1.1: + resolution: {integrity: sha512-nuL+dNSVMcWB7OOtgb0EGH5AjO4UBCt68SLP08rwmC+iRhyuJWS9MtZ/MpipxFwKAlHFftbMsydXqWre8B0+XA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/test-result': 27.0.6 - graceful-fs: 4.2.6 - jest-haste-map: 27.0.6 - jest-runtime: 27.0.6 - transitivePeerDependencies: - - supports-color + '@jest/test-result': 28.1.1 + graceful-fs: 4.2.10 + jest-haste-map: 28.1.1 + slash: 3.0.0 dev: true /@jest/transform/26.6.2: @@ -657,7 +685,7 @@ packages: chalk: 4.1.1 convert-source-map: 1.8.0 fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 jest-haste-map: 26.6.2 jest-regex-util: 26.0.0 jest-util: 26.6.2 @@ -670,25 +698,25 @@ packages: - supports-color dev: true - /@jest/transform/27.0.6: - resolution: {integrity: sha512-rj5Dw+mtIcntAUnMlW/Vju5mr73u8yg+irnHwzgtgoeI6cCPOvUwQ0D1uQtc/APmWgvRweEb1g05pkUpxH3iCA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/transform/28.1.2: + resolution: {integrity: sha512-3o+lKF6iweLeJFHBlMJysdaPbpoMmtbHEFsjzSv37HIq/wWt5ijTeO2Yf7MO5yyczCopD507cNwNLeX8Y/CuIg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@babel/core': 7.14.8 - '@jest/types': 27.0.6 - babel-plugin-istanbul: 6.0.0 + '@jest/types': 28.1.1 + '@jridgewell/trace-mapping': 0.3.14 + babel-plugin-istanbul: 6.1.1 chalk: 4.1.1 convert-source-map: 1.8.0 fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.6 - jest-haste-map: 27.0.6 - jest-regex-util: 27.0.6 - jest-util: 27.0.6 + graceful-fs: 4.2.10 + jest-haste-map: 28.1.1 + jest-regex-util: 28.0.2 + jest-util: 28.1.1 micromatch: 4.0.4 - pirates: 4.0.1 + pirates: 4.0.5 slash: 3.0.0 - source-map: 0.6.1 - write-file-atomic: 3.0.3 + write-file-atomic: 4.0.1 transitivePeerDependencies: - supports-color dev: true @@ -704,47 +732,66 @@ packages: chalk: 4.1.1 dev: true - /@jest/types/27.0.6: - resolution: {integrity: sha512-aSquT1qa9Pik26JK5/3rvnYb4bGtm1VFNesHKmNTwmPIgOrixvhL2ghIvFRNEpzy3gU+rUgjIF/KodbkFAl++g==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /@jest/types/28.1.1: + resolution: {integrity: sha512-vRXVqSg1VhDnB8bWcmvLzmg0Bt9CRKVgHPXqYwvWMX3TvAjeO+nRuK6+VdTKCtWOvYlmkF/HqNAL/z+N3B53Kw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: + '@jest/schemas': 28.0.2 '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.1 '@types/node': 16.4.3 - '@types/yargs': 16.0.4 + '@types/yargs': 17.0.10 chalk: 4.1.1 dev: true - /@nick-mazuk/eslint-config/0.8.13_eslint@7.31.0+typescript@4.3.5: + /@jridgewell/resolve-uri/3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec/1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/trace-mapping/0.3.14: + resolution: {integrity: sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@nick-mazuk/eslint-config/0.8.13_6r4pv37zydmmxnib4wi3jja2me: resolution: {integrity: sha512-9ekw5u7UgUOUbAIEvXPNEg4j6AXEWpdjVNzYmtiOo+9tJ844JbLFQkcDbx93DV2cGUPd5Zosju2S+NjBQ04nug==} peerDependencies: eslint: '>= 7.25.0' typescript: '>= 4.2.4' dependencies: - '@typescript-eslint/eslint-plugin': 4.28.4_b1648df9f9ba40bdeef3710a5a5af353 - '@typescript-eslint/parser': 4.28.4_eslint@7.31.0+typescript@4.3.5 + '@typescript-eslint/eslint-plugin': 4.28.4_ojtn7elzzqayji7yuno3vktuiq + '@typescript-eslint/parser': 4.28.4_6r4pv37zydmmxnib4wi3jja2me eslint: 7.31.0 eslint-config-prettier: 8.3.0_eslint@7.31.0 eslint-plugin-chai-friendly: 0.7.1_eslint@7.31.0 eslint-plugin-cypress: 2.11.3_eslint@7.31.0 - eslint-plugin-deprecation: 1.2.1_eslint@7.31.0+typescript@4.3.5 + eslint-plugin-deprecation: 1.2.1_6r4pv37zydmmxnib4wi3jja2me eslint-plugin-eslint-comments: 3.2.0_eslint@7.31.0 - eslint-plugin-import: 2.23.4_eslint@7.31.0 - eslint-plugin-jest: 24.4.0_fc5326c9e782cff3be563ae5197052dc + eslint-plugin-import: 2.23.4_uzlqeu5veffmcxyhhc5bjfrl5i + eslint-plugin-jest: 24.4.0_wmjus5k32hjkt2pxdljurygoby eslint-plugin-jsx-a11y: 6.4.1_eslint@7.31.0 eslint-plugin-no-secrets: 0.8.9_eslint@7.31.0 - eslint-plugin-prettier: 3.4.0_19f511d6aa08b367b6cb59e8f50291ca + eslint-plugin-prettier: 3.4.0_dh2rdvvkbczwpnwllhupkaurzi eslint-plugin-promise: 5.1.0_eslint@7.31.0 eslint-plugin-react: 7.24.0_eslint@7.31.0 eslint-plugin-react-hooks: 4.2.0_eslint@7.31.0 eslint-plugin-sonarjs: 0.9.1_eslint@7.31.0 eslint-plugin-svelte3: 3.2.0_eslint@7.31.0 - eslint-plugin-testing-library: 4.10.1_eslint@7.31.0+typescript@4.3.5 + eslint-plugin-testing-library: 4.10.1_6r4pv37zydmmxnib4wi3jja2me eslint-plugin-unicorn: 34.0.1_eslint@7.31.0 eslint-plugin-unused-imports: 1.1.2 prettier: 2.3.2 - typescript: 4.3.5 + typescript: 4.7.4 transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack - supports-color - svelte dev: true @@ -770,23 +817,22 @@ packages: fastq: 1.11.1 dev: true + /@sinclair/typebox/0.23.5: + resolution: {integrity: sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg==} + dev: true + /@sinonjs/commons/1.8.3: resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==} dependencies: type-detect: 4.0.8 dev: true - /@sinonjs/fake-timers/7.1.2: - resolution: {integrity: sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==} + /@sinonjs/fake-timers/9.1.2: + resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} dependencies: '@sinonjs/commons': 1.8.3 dev: true - /@tootallnate/once/1.1.2: - resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} - engines: {node: '>= 6'} - dev: true - /@types/babel__core/7.1.15: resolution: {integrity: sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew==} dependencies: @@ -887,13 +933,13 @@ packages: '@types/yargs-parser': 20.2.1 dev: true - /@types/yargs/16.0.4: - resolution: {integrity: sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==} + /@types/yargs/17.0.10: + resolution: {integrity: sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==} dependencies: '@types/yargs-parser': 20.2.1 dev: true - /@typescript-eslint/eslint-plugin/4.28.4_b1648df9f9ba40bdeef3710a5a5af353: + /@typescript-eslint/eslint-plugin/4.28.4_ojtn7elzzqayji7yuno3vktuiq: resolution: {integrity: sha512-s1oY4RmYDlWMlcV0kKPBaADn46JirZzvvH7c2CtAqxCY96S538JRBAzt83RrfkDheV/+G/vWNK0zek+8TB3Gmw==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -904,21 +950,21 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/experimental-utils': 4.28.4_eslint@7.31.0+typescript@4.3.5 - '@typescript-eslint/parser': 4.28.4_eslint@7.31.0+typescript@4.3.5 + '@typescript-eslint/experimental-utils': 4.28.4_6r4pv37zydmmxnib4wi3jja2me + '@typescript-eslint/parser': 4.28.4_6r4pv37zydmmxnib4wi3jja2me '@typescript-eslint/scope-manager': 4.28.4 debug: 4.3.2 eslint: 7.31.0 functional-red-black-tree: 1.0.1 regexpp: 3.2.0 semver: 7.3.5 - tsutils: 3.21.0_typescript@4.3.5 - typescript: 4.3.5 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/experimental-utils/3.10.1_eslint@7.31.0+typescript@4.3.5: + /@typescript-eslint/experimental-utils/3.10.1_6r4pv37zydmmxnib4wi3jja2me: resolution: {integrity: sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -926,7 +972,7 @@ packages: dependencies: '@types/json-schema': 7.0.8 '@typescript-eslint/types': 3.10.1 - '@typescript-eslint/typescript-estree': 3.10.1_typescript@4.3.5 + '@typescript-eslint/typescript-estree': 3.10.1_typescript@4.7.4 eslint: 7.31.0 eslint-scope: 5.1.1 eslint-utils: 2.1.0 @@ -935,7 +981,7 @@ packages: - typescript dev: true - /@typescript-eslint/experimental-utils/4.28.4_eslint@7.31.0+typescript@4.3.5: + /@typescript-eslint/experimental-utils/4.28.4_6r4pv37zydmmxnib4wi3jja2me: resolution: {integrity: sha512-OglKWOQRWTCoqMSy6pm/kpinEIgdcXYceIcH3EKWUl4S8xhFtN34GQRaAvTIZB9DD94rW7d/U7tUg3SYeDFNHA==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -944,7 +990,7 @@ packages: '@types/json-schema': 7.0.8 '@typescript-eslint/scope-manager': 4.28.4 '@typescript-eslint/types': 4.28.4 - '@typescript-eslint/typescript-estree': 4.28.4_typescript@4.3.5 + '@typescript-eslint/typescript-estree': 4.28.4_typescript@4.7.4 eslint: 7.31.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0_eslint@7.31.0 @@ -953,7 +999,7 @@ packages: - typescript dev: true - /@typescript-eslint/parser/4.28.4_eslint@7.31.0+typescript@4.3.5: + /@typescript-eslint/parser/4.28.4_6r4pv37zydmmxnib4wi3jja2me: resolution: {integrity: sha512-4i0jq3C6n+og7/uCHiE6q5ssw87zVdpUj1k6VlVYMonE3ILdFApEzTWgppSRG4kVNB/5jxnH+gTeKLMNfUelQA==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -965,10 +1011,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 4.28.4 '@typescript-eslint/types': 4.28.4 - '@typescript-eslint/typescript-estree': 4.28.4_typescript@4.3.5 + '@typescript-eslint/typescript-estree': 4.28.4_typescript@4.7.4 debug: 4.3.2 eslint: 7.31.0 - typescript: 4.3.5 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true @@ -991,7 +1037,7 @@ packages: engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} dev: true - /@typescript-eslint/typescript-estree/3.10.1_typescript@4.3.5: + /@typescript-eslint/typescript-estree/3.10.1_typescript@4.7.4: resolution: {integrity: sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -1007,13 +1053,13 @@ packages: is-glob: 4.0.1 lodash: 4.17.21 semver: 7.3.5 - tsutils: 3.21.0_typescript@4.3.5 - typescript: 4.3.5 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree/4.28.4_typescript@4.3.5: + /@typescript-eslint/typescript-estree/4.28.4_typescript@4.7.4: resolution: {integrity: sha512-z7d8HK8XvCRyN2SNp+OXC2iZaF+O2BTquGhEYLKLx5k6p0r05ureUtgEfo5f6anLkhCxdHtCf6rPM1p4efHYDQ==} engines: {node: ^10.12.0 || >=12.0.0} peerDependencies: @@ -1028,8 +1074,8 @@ packages: globby: 11.0.4 is-glob: 4.0.1 semver: 7.3.5 - tsutils: 3.21.0_typescript@4.3.5 - typescript: 4.3.5 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true @@ -1049,22 +1095,11 @@ packages: eslint-visitor-keys: 2.1.0 dev: true - /@vercel/ncc/0.29.0: - resolution: {integrity: sha512-p+sB835wOSDdgm2mgFgSOcXJF84AqZ+vBEnnGS0sm8veA92Hia7sqH0qEnqeFilPl+cXtxbdh2er+WdlfbVCZA==} + /@vercel/ncc/0.34.0: + resolution: {integrity: sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==} hasBin: true dev: true - /abab/2.0.5: - resolution: {integrity: sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==} - dev: true - - /acorn-globals/6.0.0: - resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} - dependencies: - acorn: 7.4.1 - acorn-walk: 7.2.0 - dev: true - /acorn-jsx/5.3.2_acorn@7.4.1: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1073,32 +1108,12 @@ packages: acorn: 7.4.1 dev: true - /acorn-walk/7.2.0: - resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} - engines: {node: '>=0.4.0'} - dev: true - /acorn/7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /acorn/8.4.1: - resolution: {integrity: sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /agent-base/6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.2 - transitivePeerDependencies: - - supports-color - dev: true - /ajv/6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: @@ -1134,6 +1149,11 @@ packages: engines: {node: '>=8'} dev: true + /ansi-regex/5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + /ansi-styles/3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -1158,6 +1178,8 @@ packages: dependencies: micromatch: 3.1.10 normalize-path: 2.1.1 + transitivePeerDependencies: + - supports-color dev: true /anymatch/3.1.2: @@ -1183,7 +1205,7 @@ packages: dev: true /arr-diff/4.0.0: - resolution: {integrity: sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=} + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} engines: {node: '>=0.10.0'} dev: true @@ -1193,7 +1215,7 @@ packages: dev: true /arr-union/3.1.0: - resolution: {integrity: sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=} + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} engines: {node: '>=0.10.0'} dev: true @@ -1214,7 +1236,7 @@ packages: dev: true /array-unique/0.3.2: - resolution: {integrity: sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=} + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} engines: {node: '>=0.10.0'} dev: true @@ -1238,7 +1260,7 @@ packages: dev: true /assign-symbols/1.0.0: - resolution: {integrity: sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=} + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} engines: {node: '>=0.10.0'} dev: true @@ -1251,10 +1273,6 @@ packages: engines: {node: '>=8'} dev: true - /asynckit/0.4.0: - resolution: {integrity: sha1-x57Zf380y48robyXkLzDZkdLS3k=} - dev: true - /atob/2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} engines: {node: '>= 4.5.0'} @@ -1289,20 +1307,19 @@ packages: - supports-color dev: true - /babel-jest/27.0.6_@babel+core@7.14.8: - resolution: {integrity: sha512-iTJyYLNc4wRofASmofpOc5NK9QunwMk+TLFgGXsTFS8uEqmd8wdI7sga0FPe2oVH3b5Agt/EAK1QjPEuKL8VfA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /babel-jest/28.1.2_@babel+core@7.14.8: + resolution: {integrity: sha512-pfmoo6sh4L/+5/G2OOfQrGJgvH7fTa1oChnuYH2G/6gA+JwDvO8PELwvwnofKBMNrQsam0Wy/Rw+QSrBNewq2Q==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: '@babel/core': 7.14.8 - '@jest/transform': 27.0.6 - '@jest/types': 27.0.6 + '@jest/transform': 28.1.2 '@types/babel__core': 7.1.15 - babel-plugin-istanbul: 6.0.0 - babel-preset-jest: 27.0.6_@babel+core@7.14.8 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 28.1.1_@babel+core@7.14.8 chalk: 4.1.1 - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 slash: 3.0.0 transitivePeerDependencies: - supports-color @@ -1327,6 +1344,19 @@ packages: - supports-color dev: true + /babel-plugin-istanbul/6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.0 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-jest-hoist/26.6.2: resolution: {integrity: sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==} engines: {node: '>= 10.14.2'} @@ -1337,9 +1367,9 @@ packages: '@types/babel__traverse': 7.14.2 dev: true - /babel-plugin-jest-hoist/27.0.6: - resolution: {integrity: sha512-CewFeM9Vv2gM7Yr9n5eyyLVPRSiBnk6lKZRjgwYnGKSl9M14TMn2vkN02wTF04OGuSDLEzlWiMzvjXuW9mB6Gw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /babel-plugin-jest-hoist/28.1.1: + resolution: {integrity: sha512-NovGCy5Hn25uMJSAU8FaHqzs13cFoOI4lhIujiepssjCKRsAo3TA734RDWSGxuFTsUJXerYOqQQodlxgmtqbzw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@babel/template': 7.14.5 '@babel/types': 7.14.8 @@ -1378,14 +1408,14 @@ packages: babel-preset-current-node-syntax: 1.0.1_@babel+core@7.14.8 dev: true - /babel-preset-jest/27.0.6_@babel+core@7.14.8: - resolution: {integrity: sha512-WObA0/Biw2LrVVwZkF/2GqbOdzhKD6Fkdwhoy9ASIrOWr/zodcSpQh72JOkEn6NWyjmnPDjNSqaGN4KnpKzhXw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /babel-preset-jest/28.1.1_@babel+core@7.14.8: + resolution: {integrity: sha512-FCq9Oud0ReTeWtcneYf/48981aTfXYuB9gbU4rBNNJVBSQ6ssv7E6v/qvbBxtOWwZFXjLZwpg+W3q7J6vhH25g==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.14.8 - babel-plugin-jest-hoist: 27.0.6 + babel-plugin-jest-hoist: 28.1.1 babel-preset-current-node-syntax: 1.0.1_@babel+core@7.14.8 dev: true @@ -1427,6 +1457,8 @@ packages: snapdragon-node: 2.1.1 split-string: 3.1.0 to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color dev: true /braces/3.0.2: @@ -1436,10 +1468,6 @@ packages: fill-range: 7.0.1 dev: true - /browser-process-hrtime/1.0.0: - resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} - dev: true - /browserslist/4.16.6: resolution: {integrity: sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1569,13 +1597,13 @@ packages: /cliui/7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: - string-width: 4.2.2 + string-width: 4.2.3 strip-ansi: 6.0.0 wrap-ansi: 7.0.0 dev: true /co/4.6.0: - resolution: {integrity: sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=} + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} dev: true @@ -1584,7 +1612,7 @@ packages: dev: true /collection-visit/1.0.0: - resolution: {integrity: sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=} + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} engines: {node: '>=0.10.0'} dependencies: map-visit: 1.0.0 @@ -1616,13 +1644,6 @@ packages: resolution: {integrity: sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==} dev: true - /combined-stream/1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: true - /component-emitter/1.3.0: resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} dev: true @@ -1638,7 +1659,7 @@ packages: dev: true /copy-descriptor/0.1.1: - resolution: {integrity: sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=} + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} engines: {node: '>=0.10.0'} dev: true @@ -1667,42 +1688,28 @@ packages: which: 2.0.2 dev: true - /cssom/0.3.8: - resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} - dev: true - - /cssom/0.4.4: - resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==} - dev: true - - /cssstyle/2.3.0: - resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} - engines: {node: '>=8'} - dependencies: - cssom: 0.3.8 - dev: true - /damerau-levenshtein/1.0.7: resolution: {integrity: sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==} dev: true - /data-urls/2.0.0: - resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==} - engines: {node: '>=10'} - dependencies: - abab: 2.0.5 - whatwg-mimetype: 2.3.0 - whatwg-url: 8.7.0 - dev: true - /debug/2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true dependencies: ms: 2.0.0 dev: true /debug/3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true dependencies: ms: 2.1.3 dev: true @@ -1719,17 +1726,13 @@ packages: ms: 2.1.2 dev: true - /decimal.js/10.3.1: - resolution: {integrity: sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==} - dev: true - /decode-uri-component/0.2.0: - resolution: {integrity: sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=} + resolution: {integrity: sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==} engines: {node: '>=0.10'} dev: true /dedent/0.7.0: - resolution: {integrity: sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=} + resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true /deep-is/0.1.3: @@ -1749,14 +1752,14 @@ packages: dev: true /define-property/0.2.5: - resolution: {integrity: sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=} + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} engines: {node: '>=0.10.0'} dependencies: is-descriptor: 0.1.6 dev: true /define-property/1.0.0: - resolution: {integrity: sha1-dp66rz9KY6rTr56NMEybvnm/sOY=} + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} engines: {node: '>=0.10.0'} dependencies: is-descriptor: 1.0.2 @@ -1770,11 +1773,6 @@ packages: isobject: 3.0.1 dev: true - /delayed-stream/1.0.0: - resolution: {integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk=} - engines: {node: '>=0.4.0'} - dev: true - /detect-newline/3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -1785,9 +1783,9 @@ packages: engines: {node: '>= 10.14.2'} dev: true - /diff-sequences/27.0.6: - resolution: {integrity: sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /diff-sequences/28.1.1: + resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dev: true /dir-glob/3.0.1: @@ -1811,20 +1809,13 @@ packages: esutils: 2.0.3 dev: true - /domexception/2.0.1: - resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==} - engines: {node: '>=8'} - dependencies: - webidl-conversions: 5.0.0 - dev: true - /electron-to-chromium/1.3.786: resolution: {integrity: sha512-AmvbLBj3hepRk8v/DHrFF8gINxOFfDbrn6Ts3PcK46/FBdQb5OMmpamSpZQXSkfi77FfBzYtQtAk+00LCLYMVw==} dev: true - /emittery/0.8.1: - resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} - engines: {node: '>=10'} + /emittery/0.10.2: + resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} + engines: {node: '>=12'} dev: true /emoji-regex/8.0.0: @@ -1885,7 +1876,61 @@ packages: is-symbol: 1.0.4 dev: true - /esbuild-jest/0.5.0_esbuild@0.12.15: + /esbuild-android-64/0.14.49: + resolution: {integrity: sha512-vYsdOTD+yi+kquhBiFWl3tyxnj2qZJsl4tAqwhT90ktUdnyTizgle7TjNx6Ar1bN7wcwWqZ9QInfdk2WVagSww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-android-arm64/0.14.49: + resolution: {integrity: sha512-g2HGr/hjOXCgSsvQZ1nK4nW/ei8JUx04Li74qub9qWrStlysaVmadRyTVuW32FGIpLQyc5sUjjZopj49eGGM2g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-64/0.14.49: + resolution: {integrity: sha512-3rvqnBCtX9ywso5fCHixt2GBCUsogNp9DjGmvbBohh31Ces34BVzFltMSxJpacNki96+WIcX5s/vum+ckXiLYg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-darwin-arm64/0.14.49: + resolution: {integrity: sha512-XMaqDxO846srnGlUSJnwbijV29MTKUATmOLyQSfswbK/2X5Uv28M9tTLUJcKKxzoo9lnkYPsx2o8EJcTYwCs/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-64/0.14.49: + resolution: {integrity: sha512-NJ5Q6AjV879mOHFri+5lZLTp5XsO2hQ+KSJYLbfY9DgCu8s6/Zl2prWXVANYTeCDLlrIlNNYw8y34xqyLDKOmQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-freebsd-arm64/0.14.49: + resolution: {integrity: sha512-lFLtgXnAc3eXYqj5koPlBZvEbBSOSUbWO3gyY/0+4lBdRqELyz4bAuamHvmvHW5swJYL7kngzIZw6kdu25KGOA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-jest/0.5.0_esbuild@0.14.49: resolution: {integrity: sha512-AMZZCdEpXfNVOIDvURlqYyHwC8qC1/BFjgsrOiSL1eyiIArVtHL8YAC83Shhn16cYYoAWEW17yZn0W/RJKJKHQ==} peerDependencies: esbuild: '>=0.8.50' @@ -1893,15 +1938,163 @@ packages: '@babel/core': 7.14.8 '@babel/plugin-transform-modules-commonjs': 7.14.5_@babel+core@7.14.8 babel-jest: 26.6.3_@babel+core@7.14.8 - esbuild: 0.12.15 + esbuild: 0.14.49 transitivePeerDependencies: - supports-color dev: true - /esbuild/0.12.15: - resolution: {integrity: sha512-72V4JNd2+48eOVCXx49xoSWHgC3/cCy96e7mbXKY+WOWghN00cCmlGnwVLRhRHorvv0dgCyuMYBZlM2xDM5OQw==} + /esbuild-linux-32/0.14.49: + resolution: {integrity: sha512-zTTH4gr2Kb8u4QcOpTDVn7Z8q7QEIvFl/+vHrI3cF6XOJS7iEI1FWslTo3uofB2+mn6sIJEQD9PrNZKoAAMDiA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-64/0.14.49: + resolution: {integrity: sha512-hYmzRIDzFfLrB5c1SknkxzM8LdEUOusp6M2TnuQZJLRtxTgyPnZZVtyMeCLki0wKgYPXkFsAVhi8vzo2mBNeTg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm/0.14.49: + resolution: {integrity: sha512-iE3e+ZVv1Qz1Sy0gifIsarJMQ89Rpm9mtLSRtG3AH0FPgAzQ5Z5oU6vYzhc/3gSPi2UxdCOfRhw2onXuFw/0lg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-arm64/0.14.49: + resolution: {integrity: sha512-KLQ+WpeuY+7bxukxLz5VgkAAVQxUv67Ft4DmHIPIW+2w3ObBPQhqNoeQUHxopoW/aiOn3m99NSmSV+bs4BSsdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-mips64le/0.14.49: + resolution: {integrity: sha512-n+rGODfm8RSum5pFIqFQVQpYBw+AztL8s6o9kfx7tjfK0yIGF6tm5HlG6aRjodiiKkH2xAiIM+U4xtQVZYU4rA==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-ppc64le/0.14.49: + resolution: {integrity: sha512-WP9zR4HX6iCBmMFH+XHHng2LmdoIeUmBpL4aL2TR8ruzXyT4dWrJ5BSbT8iNo6THN8lod6GOmYDLq/dgZLalGw==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-riscv64/0.14.49: + resolution: {integrity: sha512-h66ORBz+Dg+1KgLvzTVQEA1LX4XBd1SK0Fgbhhw4akpG/YkN8pS6OzYI/7SGENiN6ao5hETRDSkVcvU9NRtkMQ==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-linux-s390x/0.14.49: + resolution: {integrity: sha512-DhrUoFVWD+XmKO1y7e4kNCqQHPs6twz6VV6Uezl/XHYGzM60rBewBF5jlZjG0nCk5W/Xy6y1xWeopkrhFFM0sQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /esbuild-netbsd-64/0.14.49: + resolution: {integrity: sha512-BXaUwFOfCy2T+hABtiPUIpWjAeWK9P8O41gR4Pg73hpzoygVGnj0nI3YK4SJhe52ELgtdgWP/ckIkbn2XaTxjQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-openbsd-64/0.14.49: + resolution: {integrity: sha512-lP06UQeLDGmVPw9Rg437Btu6J9/BmyhdoefnQ4gDEJTtJvKtQaUcOQrhjTq455ouZN4EHFH1h28WOJVANK41kA==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /esbuild-sunos-64/0.14.49: + resolution: {integrity: sha512-4c8Zowp+V3zIWje329BeLbGh6XI9c/rqARNaj5yPHdC61pHI9UNdDxT3rePPJeWcEZVKjkiAS6AP6kiITp7FSw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-32/0.14.49: + resolution: {integrity: sha512-q7Rb+J9yHTeKr9QTPDYkqfkEj8/kcKz9lOabDuvEXpXuIcosWCJgo5Z7h/L4r7rbtTH4a8U2FGKb6s1eeOHmJA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-64/0.14.49: + resolution: {integrity: sha512-+Cme7Ongv0UIUTniPqfTX6mJ8Deo7VXw9xN0yJEN1lQMHDppTNmKwAM3oGbD/Vqff+07K2gN0WfNkMohmG+dVw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild-windows-arm64/0.14.49: + resolution: {integrity: sha512-v+HYNAXzuANrCbbLFJ5nmO3m5y2PGZWLe3uloAkLt87aXiO2mZr3BTmacZdjwNkNEHuH3bNtN8cak+mzVjVPfA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /esbuild/0.14.49: + resolution: {integrity: sha512-/TlVHhOaq7Yz8N1OJrjqM3Auzo5wjvHFLk+T8pIue+fhnhIMpfAzsG6PLVMbFveVxqD2WOp3QHei+52IMUNmCw==} + engines: {node: '>=12'} hasBin: true requiresBuild: true + optionalDependencies: + esbuild-android-64: 0.14.49 + esbuild-android-arm64: 0.14.49 + esbuild-darwin-64: 0.14.49 + esbuild-darwin-arm64: 0.14.49 + esbuild-freebsd-64: 0.14.49 + esbuild-freebsd-arm64: 0.14.49 + esbuild-linux-32: 0.14.49 + esbuild-linux-64: 0.14.49 + esbuild-linux-arm: 0.14.49 + esbuild-linux-arm64: 0.14.49 + esbuild-linux-mips64le: 0.14.49 + esbuild-linux-ppc64le: 0.14.49 + esbuild-linux-riscv64: 0.14.49 + esbuild-linux-s390x: 0.14.49 + esbuild-netbsd-64: 0.14.49 + esbuild-openbsd-64: 0.14.49 + esbuild-sunos-64: 0.14.49 + esbuild-windows-32: 0.14.49 + esbuild-windows-64: 0.14.49 + esbuild-windows-arm64: 0.14.49 dev: true /escalade/3.1.1: @@ -1924,19 +2117,6 @@ packages: engines: {node: '>=10'} dev: true - /escodegen/2.0.0: - resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==} - engines: {node: '>=6.0'} - hasBin: true - dependencies: - esprima: 4.0.1 - estraverse: 5.2.0 - esutils: 2.0.3 - optionator: 0.8.3 - optionalDependencies: - source-map: 0.6.1 - dev: true - /eslint-config-prettier/8.3.0_eslint@7.31.0: resolution: {integrity: sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==} hasBin: true @@ -1951,14 +2131,34 @@ packages: dependencies: debug: 2.6.9 resolve: 1.20.0 + transitivePeerDependencies: + - supports-color dev: true - /eslint-module-utils/2.6.1: + /eslint-module-utils/2.6.1_4cetwch6gwi5il2ctfsroesthy: resolution: {integrity: sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==} engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true dependencies: + '@typescript-eslint/parser': 4.28.4_6r4pv37zydmmxnib4wi3jja2me debug: 3.2.7 + eslint-import-resolver-node: 0.3.4 pkg-dir: 2.0.0 + transitivePeerDependencies: + - supports-color dev: true /eslint-plugin-chai-friendly/0.7.1_eslint@7.31.0: @@ -1979,17 +2179,17 @@ packages: globals: 11.12.0 dev: true - /eslint-plugin-deprecation/1.2.1_eslint@7.31.0+typescript@4.3.5: + /eslint-plugin-deprecation/1.2.1_6r4pv37zydmmxnib4wi3jja2me: resolution: {integrity: sha512-8KFAWPO3AvF0szxIh1ivRtHotd1fzxVOuNR3NI8dfCsQKgcxu9fAgEY+eTKvCRLAwwI8kaDDfImMt+498+EgRw==} peerDependencies: eslint: ^6.0.0 || ^7.0.0 typescript: ^3.7.5 || ^4.0.0 dependencies: - '@typescript-eslint/experimental-utils': 3.10.1_eslint@7.31.0+typescript@4.3.5 + '@typescript-eslint/experimental-utils': 3.10.1_6r4pv37zydmmxnib4wi3jja2me eslint: 7.31.0 tslib: 1.14.1 - tsutils: 3.21.0_typescript@4.3.5 - typescript: 4.3.5 + tsutils: 3.21.0_typescript@4.7.4 + typescript: 4.7.4 transitivePeerDependencies: - supports-color dev: true @@ -2005,19 +2205,24 @@ packages: ignore: 5.1.8 dev: true - /eslint-plugin-import/2.23.4_eslint@7.31.0: + /eslint-plugin-import/2.23.4_uzlqeu5veffmcxyhhc5bjfrl5i: resolution: {integrity: sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==} engines: {node: '>=4'} peerDependencies: + '@typescript-eslint/parser': '*' eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true dependencies: + '@typescript-eslint/parser': 4.28.4_6r4pv37zydmmxnib4wi3jja2me array-includes: 3.1.3 array.prototype.flat: 1.2.4 debug: 2.6.9 doctrine: 2.1.0 eslint: 7.31.0 eslint-import-resolver-node: 0.3.4 - eslint-module-utils: 2.6.1 + eslint-module-utils: 2.6.1_4cetwch6gwi5il2ctfsroesthy find-up: 2.1.0 has: 1.0.3 is-core-module: 2.5.0 @@ -2027,9 +2232,13 @@ packages: read-pkg-up: 3.0.0 resolve: 1.20.0 tsconfig-paths: 3.10.1 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color dev: true - /eslint-plugin-jest/24.4.0_fc5326c9e782cff3be563ae5197052dc: + /eslint-plugin-jest/24.4.0_wmjus5k32hjkt2pxdljurygoby: resolution: {integrity: sha512-8qnt/hgtZ94E9dA6viqfViKBfkJwFHXgJmTWlMGDgunw1XJEGqm3eiPjDsTanM3/u/3Az82nyQM9GX7PM/QGmg==} engines: {node: '>=10'} peerDependencies: @@ -2039,8 +2248,8 @@ packages: '@typescript-eslint/eslint-plugin': optional: true dependencies: - '@typescript-eslint/eslint-plugin': 4.28.4_b1648df9f9ba40bdeef3710a5a5af353 - '@typescript-eslint/experimental-utils': 4.28.4_eslint@7.31.0+typescript@4.3.5 + '@typescript-eslint/eslint-plugin': 4.28.4_ojtn7elzzqayji7yuno3vktuiq + '@typescript-eslint/experimental-utils': 4.28.4_6r4pv37zydmmxnib4wi3jja2me eslint: 7.31.0 transitivePeerDependencies: - supports-color @@ -2076,7 +2285,7 @@ packages: eslint: 7.31.0 dev: true - /eslint-plugin-prettier/3.4.0_19f511d6aa08b367b6cb59e8f50291ca: + /eslint-plugin-prettier/3.4.0_dh2rdvvkbczwpnwllhupkaurzi: resolution: {integrity: sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==} engines: {node: '>=6.0.0'} peerDependencies: @@ -2151,13 +2360,13 @@ packages: eslint: 7.31.0 dev: true - /eslint-plugin-testing-library/4.10.1_eslint@7.31.0+typescript@4.3.5: + /eslint-plugin-testing-library/4.10.1_6r4pv37zydmmxnib4wi3jja2me: resolution: {integrity: sha512-pISDdbDBTAkd6nnAoMIMLsU91UBh6l3UX5n0FdUjGM0D92WLw+z/0WR4iptO06G2UhkwcTNl1r/K4huS3/gsXA==} engines: {node: ^10.12.0 || >=12.0.0, npm: '>=6'} peerDependencies: eslint: ^7.5.0 dependencies: - '@typescript-eslint/experimental-utils': 4.28.4_eslint@7.31.0+typescript@4.3.5 + '@typescript-eslint/experimental-utils': 4.28.4_6r4pv37zydmmxnib4wi3jja2me eslint: 7.31.0 transitivePeerDependencies: - supports-color @@ -2213,7 +2422,7 @@ packages: eslint: '>=7.0.0' dependencies: '@babel/core': 7.14.8 - '@babel/eslint-parser': 7.14.7_@babel+core@7.14.8+eslint@7.31.0 + '@babel/eslint-parser': 7.14.7_xplm2s2mesxy5ln3rqv4brcbla eslint: 7.31.0 eslint-visitor-keys: 2.1.0 esquery: 1.4.0 @@ -2355,7 +2564,7 @@ packages: is-stream: 1.1.0 npm-run-path: 2.0.2 p-finally: 1.0.0 - signal-exit: 3.0.3 + signal-exit: 3.0.7 strip-eof: 1.0.0 dev: true @@ -2375,12 +2584,12 @@ packages: dev: true /exit/0.1.2: - resolution: {integrity: sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=} + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} dev: true /expand-brackets/2.1.4: - resolution: {integrity: sha1-t3c14xXOMPa27/D4OwQVGiJEliI=} + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} engines: {node: '>=0.10.0'} dependencies: debug: 2.6.9 @@ -2390,29 +2599,30 @@ packages: regex-not: 1.0.2 snapdragon: 0.8.2 to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color dev: true - /expect/27.0.6: - resolution: {integrity: sha512-psNLt8j2kwg42jGBDSfAlU49CEZxejN1f1PlANWDZqIhBOVU/c2Pm888FcjWJzFewhIsNWfZJeLjUjtKGiPuSw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /expect/28.1.1: + resolution: {integrity: sha512-/AANEwGL0tWBwzLNOvO0yUdy2D52jVdNXppOqswC49sxMN2cPWsGCQdzuIf9tj6hHoBQzNvx75JUYuQAckPo3w==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 - ansi-styles: 5.2.0 - jest-get-type: 27.0.6 - jest-matcher-utils: 27.0.6 - jest-message-util: 27.0.6 - jest-regex-util: 27.0.6 + '@jest/expect-utils': 28.1.1 + jest-get-type: 28.0.2 + jest-matcher-utils: 28.1.1 + jest-message-util: 28.1.1 + jest-util: 28.1.1 dev: true /extend-shallow/2.0.1: - resolution: {integrity: sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=} + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} dependencies: is-extendable: 0.1.1 dev: true /extend-shallow/3.0.2: - resolution: {integrity: sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=} + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} engines: {node: '>=0.10.0'} dependencies: assign-symbols: 1.0.0 @@ -2431,6 +2641,8 @@ packages: regex-not: 1.0.2 snapdragon: 0.8.2 to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color dev: true /fast-deep-equal/3.1.3: @@ -2480,7 +2692,7 @@ packages: dev: true /fill-range/4.0.0: - resolution: {integrity: sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=} + resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} engines: {node: '>=0.10.0'} dependencies: extend-shallow: 2.0.1 @@ -2524,21 +2736,12 @@ packages: dev: true /for-in/1.0.2: - resolution: {integrity: sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=} + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} engines: {node: '>=0.10.0'} dev: true - /form-data/3.0.1: - resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.31 - dev: true - /fragment-cache/0.2.1: - resolution: {integrity: sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=} + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} engines: {node: '>=0.10.0'} dependencies: map-cache: 0.2.2 @@ -2561,6 +2764,7 @@ packages: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + requiresBuild: true dev: true optional: true @@ -2608,7 +2812,7 @@ packages: dev: true /get-value/2.0.6: - resolution: {integrity: sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=} + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} engines: {node: '>=0.10.0'} dev: true @@ -2654,6 +2858,9 @@ packages: slash: 3.0.0 dev: true + /graceful-fs/4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + /graceful-fs/4.2.6: resolution: {integrity: sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==} @@ -2677,7 +2884,7 @@ packages: dev: true /has-value/0.3.1: - resolution: {integrity: sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=} + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} engines: {node: '>=0.10.0'} dependencies: get-value: 2.0.6 @@ -2686,7 +2893,7 @@ packages: dev: true /has-value/1.0.0: - resolution: {integrity: sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=} + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} engines: {node: '>=0.10.0'} dependencies: get-value: 2.0.6 @@ -2695,12 +2902,12 @@ packages: dev: true /has-values/0.1.4: - resolution: {integrity: sha1-bWHeldkd/Km5oCCJrThL/49it3E=} + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} engines: {node: '>=0.10.0'} dev: true /has-values/1.0.0: - resolution: {integrity: sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=} + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} engines: {node: '>=0.10.0'} dependencies: is-number: 3.0.0 @@ -2718,50 +2925,15 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true - /html-encoding-sniffer/2.0.1: - resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==} - engines: {node: '>=10'} - dependencies: - whatwg-encoding: 1.0.5 - dev: true - /html-escaper/2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true - /http-proxy-agent/4.0.1: - resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} - engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 - debug: 4.3.2 - transitivePeerDependencies: - - supports-color - dev: true - - /https-proxy-agent/5.0.0: - resolution: {integrity: sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.2 - transitivePeerDependencies: - - supports-color - dev: true - /human-signals/2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} dev: true - /iconv-lite/0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - dev: true - /ignore/4.0.6: resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} engines: {node: '>= 4'} @@ -2815,7 +2987,7 @@ packages: dev: true /is-accessor-descriptor/0.1.6: - resolution: {integrity: sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=} + resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==} engines: {node: '>=0.10.0'} dependencies: kind-of: 3.2.2 @@ -2866,13 +3038,6 @@ packages: ci-info: 2.0.0 dev: true - /is-ci/3.0.0: - resolution: {integrity: sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==} - hasBin: true - dependencies: - ci-info: 3.2.0 - dev: true - /is-core-module/2.5.0: resolution: {integrity: sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==} dependencies: @@ -2880,7 +3045,7 @@ packages: dev: true /is-data-descriptor/0.1.4: - resolution: {integrity: sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=} + resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} engines: {node: '>=0.10.0'} dependencies: kind-of: 3.2.2 @@ -2917,7 +3082,7 @@ packages: dev: true /is-extendable/0.1.1: - resolution: {integrity: sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=} + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} dev: true @@ -2961,7 +3126,7 @@ packages: dev: true /is-number/3.0.0: - resolution: {integrity: sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=} + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} engines: {node: '>=0.10.0'} dependencies: kind-of: 3.2.2 @@ -2979,10 +3144,6 @@ packages: isobject: 3.0.1 dev: true - /is-potential-custom-element-name/1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - dev: true - /is-regex/1.1.3: resolution: {integrity: sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==} engines: {node: '>= 0.4'} @@ -2992,7 +3153,7 @@ packages: dev: true /is-stream/1.1.0: - resolution: {integrity: sha1-EtSj3U5o4Lec6428hBc66A2RykQ=} + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} dev: true @@ -3014,7 +3175,7 @@ packages: dev: true /is-typedarray/1.0.0: - resolution: {integrity: sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=} + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: true /is-windows/1.0.2: @@ -3031,14 +3192,14 @@ packages: dev: true /isobject/2.1.0: - resolution: {integrity: sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=} + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} engines: {node: '>=0.10.0'} dependencies: isarray: 1.0.0 dev: true /isobject/3.0.1: - resolution: {integrity: sha1-TkMekrEalzFjaqH5yNHMvP2reN8=} + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} dev: true @@ -3047,6 +3208,11 @@ packages: engines: {node: '>=8'} dev: true + /istanbul-lib-coverage/3.2.0: + resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + engines: {node: '>=8'} + dev: true + /istanbul-lib-instrument/4.0.3: resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} engines: {node: '>=8'} @@ -3059,6 +3225,19 @@ packages: - supports-color dev: true + /istanbul-lib-instrument/5.2.0: + resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.14.8 + '@babel/parser': 7.14.8 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /istanbul-lib-report/3.0.0: resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} engines: {node: '>=8'} @@ -3073,49 +3252,48 @@ packages: engines: {node: '>=8'} dependencies: debug: 4.3.2 - istanbul-lib-coverage: 3.0.0 + istanbul-lib-coverage: 3.2.0 source-map: 0.6.1 transitivePeerDependencies: - supports-color dev: true - /istanbul-reports/3.0.2: - resolution: {integrity: sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==} + /istanbul-reports/3.1.4: + resolution: {integrity: sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==} engines: {node: '>=8'} dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.0 dev: true - /jest-changed-files/27.0.6: - resolution: {integrity: sha512-BuL/ZDauaq5dumYh5y20sn4IISnf1P9A0TDswTxUi84ORGtVa86ApuBHqICL0vepqAnZiY6a7xeSPWv2/yy4eA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-changed-files/28.0.2: + resolution: {integrity: sha512-QX9u+5I2s54ZnGoMEjiM2WeBvJR2J7w/8ZUmH2um/WLAuGAYFQcsVXY9+1YL6k0H/AGUdH8pXUAv6erDqEsvIA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 execa: 5.1.1 throat: 6.0.1 dev: true - /jest-circus/27.0.6: - resolution: {integrity: sha512-OJlsz6BBeX9qR+7O9lXefWoc2m9ZqcZ5Ohlzz0pTEAG4xMiZUJoacY8f4YDHxgk0oKYxj277AfOk9w6hZYvi1Q==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-circus/28.1.2: + resolution: {integrity: sha512-E2vdPIJG5/69EMpslFhaA46WkcrN74LI5V/cSJ59L7uS8UNoXbzTxmwhpi9XrIL3zqvMt5T0pl5k2l2u2GwBNQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/environment': 27.0.6 - '@jest/test-result': 27.0.6 - '@jest/types': 27.0.6 + '@jest/environment': 28.1.2 + '@jest/expect': 28.1.2 + '@jest/test-result': 28.1.1 + '@jest/types': 28.1.1 '@types/node': 16.4.3 chalk: 4.1.1 co: 4.6.0 dedent: 0.7.0 - expect: 27.0.6 is-generator-fn: 2.1.0 - jest-each: 27.0.6 - jest-matcher-utils: 27.0.6 - jest-message-util: 27.0.6 - jest-runtime: 27.0.6 - jest-snapshot: 27.0.6 - jest-util: 27.0.6 - pretty-format: 27.0.6 + jest-each: 28.1.1 + jest-matcher-utils: 28.1.1 + jest-message-util: 28.1.1 + jest-runtime: 28.1.2 + jest-snapshot: 28.1.2 + jest-util: 28.1.1 + pretty-format: 28.1.1 slash: 3.0.0 stack-utils: 2.0.3 throat: 6.0.1 @@ -3123,9 +3301,9 @@ packages: - supports-color dev: true - /jest-cli/27.0.6: - resolution: {integrity: sha512-qUUVlGb9fdKir3RDE+B10ULI+LQrz+MCflEH2UJyoUjoHHCbxDrMxSzjQAPUMsic4SncI62ofYCcAvW6+6rhhg==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-cli/28.1.2_@types+node@16.4.3: + resolution: {integrity: sha512-l6eoi5Do/IJUXAFL9qRmDiFpBeEJAnjJb1dcd9i/VWfVWbp3mJhuH50dNtX67Ali4Ecvt4eBkWb4hXhPHkAZTw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -3133,61 +3311,61 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 27.0.6 - '@jest/test-result': 27.0.6 - '@jest/types': 27.0.6 + '@jest/core': 28.1.2 + '@jest/test-result': 28.1.1 + '@jest/types': 28.1.1 chalk: 4.1.1 exit: 0.1.2 - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 import-local: 3.0.2 - jest-config: 27.0.6 - jest-util: 27.0.6 - jest-validate: 27.0.6 + jest-config: 28.1.2_@types+node@16.4.3 + jest-util: 28.1.1 + jest-validate: 28.1.1 prompts: 2.4.1 - yargs: 16.2.0 + yargs: 17.5.1 transitivePeerDependencies: - - bufferutil - - canvas + - '@types/node' - supports-color - ts-node - - utf-8-validate dev: true - /jest-config/27.0.6: - resolution: {integrity: sha512-JZRR3I1Plr2YxPBhgqRspDE2S5zprbga3swYNrvY3HfQGu7p/GjyLOqwrYad97tX3U3mzT53TPHVmozacfP/3w==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-config/28.1.2_@types+node@16.4.3: + resolution: {integrity: sha512-g6EfeRqddVbjPVBVY4JWpUY4IvQoFRIZcv4V36QkqzE0IGhEC/VkugFeBMAeUE7PRgC8KJF0yvJNDeQRbamEVA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} peerDependencies: + '@types/node': '*' ts-node: '>=9.0.0' peerDependenciesMeta: + '@types/node': + optional: true ts-node: optional: true dependencies: '@babel/core': 7.14.8 - '@jest/test-sequencer': 27.0.6 - '@jest/types': 27.0.6 - babel-jest: 27.0.6_@babel+core@7.14.8 + '@jest/test-sequencer': 28.1.1 + '@jest/types': 28.1.1 + '@types/node': 16.4.3 + babel-jest: 28.1.2_@babel+core@7.14.8 chalk: 4.1.1 + ci-info: 3.2.0 deepmerge: 4.2.2 glob: 7.1.7 - graceful-fs: 4.2.6 - is-ci: 3.0.0 - jest-circus: 27.0.6 - jest-environment-jsdom: 27.0.6 - jest-environment-node: 27.0.6 - jest-get-type: 27.0.6 - jest-jasmine2: 27.0.6 - jest-regex-util: 27.0.6 - jest-resolve: 27.0.6 - jest-runner: 27.0.6 - jest-util: 27.0.6 - jest-validate: 27.0.6 + graceful-fs: 4.2.10 + jest-circus: 28.1.2 + jest-environment-node: 28.1.2 + jest-get-type: 28.0.2 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.1 + jest-runner: 28.1.2 + jest-util: 28.1.1 + jest-validate: 28.1.1 micromatch: 4.0.4 - pretty-format: 27.0.6 + parse-json: 5.2.0 + pretty-format: 28.1.1 + slash: 3.0.0 + strip-json-comments: 3.1.1 transitivePeerDependencies: - - bufferutil - - canvas - supports-color - - utf-8-validate dev: true /jest-diff/26.6.2: @@ -3200,62 +3378,44 @@ packages: pretty-format: 26.6.2 dev: true - /jest-diff/27.0.6: - resolution: {integrity: sha512-Z1mqgkTCSYaFgwTlP/NUiRzdqgxmmhzHY1Tq17zL94morOHfHu3K4bgSgl+CR4GLhpV8VxkuOYuIWnQ9LnFqmg==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-diff/28.1.1: + resolution: {integrity: sha512-/MUUxeR2fHbqHoMMiffe/Afm+U8U4olFRJ0hiVG2lZatPJcnGxx292ustVu7bULhjV65IYMxRdploAKLbcrsyg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: chalk: 4.1.1 - diff-sequences: 27.0.6 - jest-get-type: 27.0.6 - pretty-format: 27.0.6 + diff-sequences: 28.1.1 + jest-get-type: 28.0.2 + pretty-format: 28.1.1 dev: true - /jest-docblock/27.0.6: - resolution: {integrity: sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-docblock/28.1.1: + resolution: {integrity: sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: detect-newline: 3.1.0 dev: true - /jest-each/27.0.6: - resolution: {integrity: sha512-m6yKcV3bkSWrUIjxkE9OC0mhBZZdhovIW5ergBYirqnkLXkyEn3oUUF/QZgyecA1cF1QFyTE8bRRl8Tfg1pfLA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-each/28.1.1: + resolution: {integrity: sha512-A042rqh17ZvEhRceDMi784ppoXR7MWGDEKTXEZXb4svt0eShMZvijGxzKsx+yIjeE8QYmHPrnHiTSQVhN4nqaw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 + '@jest/types': 28.1.1 chalk: 4.1.1 - jest-get-type: 27.0.6 - jest-util: 27.0.6 - pretty-format: 27.0.6 - dev: true - - /jest-environment-jsdom/27.0.6: - resolution: {integrity: sha512-FvetXg7lnXL9+78H+xUAsra3IeZRTiegA3An01cWeXBspKXUhAwMM9ycIJ4yBaR0L7HkoMPaZsozCLHh4T8fuw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/environment': 27.0.6 - '@jest/fake-timers': 27.0.6 - '@jest/types': 27.0.6 - '@types/node': 16.4.3 - jest-mock: 27.0.6 - jest-util: 27.0.6 - jsdom: 16.6.0 - transitivePeerDependencies: - - bufferutil - - canvas - - supports-color - - utf-8-validate + jest-get-type: 28.0.2 + jest-util: 28.1.1 + pretty-format: 28.1.1 dev: true - /jest-environment-node/27.0.6: - resolution: {integrity: sha512-+Vi6yLrPg/qC81jfXx3IBlVnDTI6kmRr08iVa2hFCWmJt4zha0XW7ucQltCAPhSR0FEKEoJ3i+W4E6T0s9is0w==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-environment-node/28.1.2: + resolution: {integrity: sha512-oYsZz9Qw27XKmOgTtnl0jW7VplJkN2oeof+SwAwKFQacq3CLlG9u4kTGuuLWfvu3J7bVutWlrbEQMOCL/jughw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/environment': 27.0.6 - '@jest/fake-timers': 27.0.6 - '@jest/types': 27.0.6 + '@jest/environment': 28.1.2 + '@jest/fake-timers': 28.1.2 + '@jest/types': 28.1.1 '@types/node': 16.4.3 - jest-mock: 27.0.6 - jest-util: 27.0.6 + jest-mock: 28.1.1 + jest-util: 28.1.1 dev: true /jest-get-type/26.3.0: @@ -3263,9 +3423,9 @@ packages: engines: {node: '>= 10.14.2'} dev: true - /jest-get-type/27.0.6: - resolution: {integrity: sha512-XTkK5exIeUbbveehcSR8w0bhH+c0yloW/Wpl+9vZrjzztCPWrxhHwkIFpZzCt71oRBsgxmuUfxEqOYoZI2macg==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-get-type/28.0.2: + resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dev: true /jest-haste-map/26.6.2: @@ -3277,106 +3437,81 @@ packages: '@types/node': 16.4.3 anymatch: 3.1.2 fb-watchman: 2.0.1 - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 jest-regex-util: 26.0.0 jest-serializer: 26.6.2 jest-util: 26.6.2 jest-worker: 26.6.2 micromatch: 4.0.4 sane: 4.1.0 - walker: 1.0.7 + walker: 1.0.8 optionalDependencies: fsevents: 2.3.2 + transitivePeerDependencies: + - supports-color dev: true - /jest-haste-map/27.0.6: - resolution: {integrity: sha512-4ldjPXX9h8doB2JlRzg9oAZ2p6/GpQUNAeiYXqcpmrKbP0Qev0wdZlxSMOmz8mPOEnt4h6qIzXFLDi8RScX/1w==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-haste-map/28.1.1: + resolution: {integrity: sha512-ZrRSE2o3Ezh7sb1KmeLEZRZ4mgufbrMwolcFHNRSjKZhpLa8TdooXOOFlSwoUzlbVs1t0l7upVRW2K7RWGHzbQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 + '@jest/types': 28.1.1 '@types/graceful-fs': 4.1.5 '@types/node': 16.4.3 anymatch: 3.1.2 fb-watchman: 2.0.1 - graceful-fs: 4.2.6 - jest-regex-util: 27.0.6 - jest-serializer: 27.0.6 - jest-util: 27.0.6 - jest-worker: 27.0.6 + graceful-fs: 4.2.10 + jest-regex-util: 28.0.2 + jest-util: 28.1.1 + jest-worker: 28.1.1 micromatch: 4.0.4 - walker: 1.0.7 + walker: 1.0.8 optionalDependencies: fsevents: 2.3.2 dev: true - /jest-jasmine2/27.0.6: - resolution: {integrity: sha512-cjpH2sBy+t6dvCeKBsHpW41mjHzXgsavaFMp+VWRf0eR4EW8xASk1acqmljFtK2DgyIECMv2yCdY41r2l1+4iA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@babel/traverse': 7.14.8 - '@jest/environment': 27.0.6 - '@jest/source-map': 27.0.6 - '@jest/test-result': 27.0.6 - '@jest/types': 27.0.6 - '@types/node': 16.4.3 - chalk: 4.1.1 - co: 4.6.0 - expect: 27.0.6 - is-generator-fn: 2.1.0 - jest-each: 27.0.6 - jest-matcher-utils: 27.0.6 - jest-message-util: 27.0.6 - jest-runtime: 27.0.6 - jest-snapshot: 27.0.6 - jest-util: 27.0.6 - pretty-format: 27.0.6 - throat: 6.0.1 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-leak-detector/27.0.6: - resolution: {integrity: sha512-2/d6n2wlH5zEcdctX4zdbgX8oM61tb67PQt4Xh8JFAIy6LRKUnX528HulkaG6nD5qDl5vRV1NXejCe1XRCH5gQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-leak-detector/28.1.1: + resolution: {integrity: sha512-4jvs8V8kLbAaotE+wFR7vfUGf603cwYtFf1/PYEsyX2BAjSzj8hQSVTP6OWzseTl0xL6dyHuKs2JAks7Pfubmw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - jest-get-type: 27.0.6 - pretty-format: 27.0.6 + jest-get-type: 28.0.2 + pretty-format: 28.1.1 dev: true - /jest-matcher-utils/27.0.6: - resolution: {integrity: sha512-OFgF2VCQx9vdPSYTHWJ9MzFCehs20TsyFi6bIHbk5V1u52zJOnvF0Y/65z3GLZHKRuTgVPY4Z6LVePNahaQ+tA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-matcher-utils/28.1.1: + resolution: {integrity: sha512-NPJPRWrbmR2nAJ+1nmnfcKKzSwgfaciCCrYZzVnNoxVoyusYWIjkBMNvu0RHJe7dNj4hH3uZOPZsQA+xAYWqsw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: chalk: 4.1.1 - jest-diff: 27.0.6 - jest-get-type: 27.0.6 - pretty-format: 27.0.6 + jest-diff: 28.1.1 + jest-get-type: 28.0.2 + pretty-format: 28.1.1 dev: true - /jest-message-util/27.0.6: - resolution: {integrity: sha512-rBxIs2XK7rGy+zGxgi+UJKP6WqQ+KrBbD1YMj517HYN3v2BG66t3Xan3FWqYHKZwjdB700KiAJ+iES9a0M+ixw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-message-util/28.1.1: + resolution: {integrity: sha512-xoDOOT66fLfmTRiqkoLIU7v42mal/SqwDKvfmfiWAdJMSJiU+ozgluO7KbvoAgiwIrrGZsV7viETjc8GNrA/IQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@babel/code-frame': 7.14.5 - '@jest/types': 27.0.6 + '@jest/types': 28.1.1 '@types/stack-utils': 2.0.1 chalk: 4.1.1 - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 micromatch: 4.0.4 - pretty-format: 27.0.6 + pretty-format: 28.1.1 slash: 3.0.0 stack-utils: 2.0.3 dev: true - /jest-mock/27.0.6: - resolution: {integrity: sha512-lzBETUoK8cSxts2NYXSBWT+EJNzmUVtVVwS1sU9GwE1DLCfGsngg+ZVSIe0yd0ZSm+y791esiuo+WSwpXJQ5Bw==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-mock/28.1.1: + resolution: {integrity: sha512-bDCb0FjfsmKweAvE09dZT59IMkzgN0fYBH6t5S45NoJfd2DHkS3ySG2K+hucortryhO3fVuXdlxWcbtIuV/Skw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 + '@jest/types': 28.1.1 '@types/node': 16.4.3 dev: true - /jest-pnp-resolver/1.2.2_jest-resolve@27.0.6: + /jest-pnp-resolver/1.2.2_jest-resolve@28.1.1: resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} engines: {node: '>=6'} peerDependencies: @@ -3385,7 +3520,7 @@ packages: jest-resolve: optional: true dependencies: - jest-resolve: 27.0.6 + jest-resolve: 28.1.1 dev: true /jest-regex-util/26.0.0: @@ -3393,100 +3528,91 @@ packages: engines: {node: '>= 10.14.2'} dev: true - /jest-regex-util/27.0.6: - resolution: {integrity: sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-regex-util/28.0.2: + resolution: {integrity: sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dev: true - /jest-resolve-dependencies/27.0.6: - resolution: {integrity: sha512-mg9x9DS3BPAREWKCAoyg3QucCr0n6S8HEEsqRCKSPjPcu9HzRILzhdzY3imsLoZWeosEbJZz6TKasveczzpJZA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-resolve-dependencies/28.1.2: + resolution: {integrity: sha512-OXw4vbOZuyRTBi3tapWBqdyodU+T33ww5cPZORuTWkg+Y8lmsxQlVu3MWtJh6NMlKRTHQetF96yGPv01Ye7Mbg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 - jest-regex-util: 27.0.6 - jest-snapshot: 27.0.6 + jest-regex-util: 28.0.2 + jest-snapshot: 28.1.2 transitivePeerDependencies: - supports-color dev: true - /jest-resolve/27.0.6: - resolution: {integrity: sha512-yKmIgw2LgTh7uAJtzv8UFHGF7Dm7XfvOe/LQ3Txv101fLM8cx2h1QVwtSJ51Q/SCxpIiKfVn6G2jYYMDNHZteA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-resolve/28.1.1: + resolution: {integrity: sha512-/d1UbyUkf9nvsgdBildLe6LAD4DalgkgZcKd0nZ8XUGPyA/7fsnaQIlKVnDiuUXv/IeZhPEDrRJubVSulxrShA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 chalk: 4.1.1 - escalade: 3.1.1 - graceful-fs: 4.2.6 - jest-pnp-resolver: 1.2.2_jest-resolve@27.0.6 - jest-util: 27.0.6 - jest-validate: 27.0.6 + graceful-fs: 4.2.10 + jest-haste-map: 28.1.1 + jest-pnp-resolver: 1.2.2_jest-resolve@28.1.1 + jest-util: 28.1.1 + jest-validate: 28.1.1 resolve: 1.20.0 + resolve.exports: 1.1.0 slash: 3.0.0 dev: true - /jest-runner/27.0.6: - resolution: {integrity: sha512-W3Bz5qAgaSChuivLn+nKOgjqNxM7O/9JOJoKDCqThPIg2sH/d4A/lzyiaFgnb9V1/w29Le11NpzTJSzga1vyYQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-runner/28.1.2: + resolution: {integrity: sha512-6/k3DlAsAEr5VcptCMdhtRhOoYClZQmxnVMZvZ/quvPGRpN7OBQYPIC32tWSgOnbgqLXNs5RAniC+nkdFZpD4A==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/console': 27.0.6 - '@jest/environment': 27.0.6 - '@jest/test-result': 27.0.6 - '@jest/transform': 27.0.6 - '@jest/types': 27.0.6 + '@jest/console': 28.1.1 + '@jest/environment': 28.1.2 + '@jest/test-result': 28.1.1 + '@jest/transform': 28.1.2 + '@jest/types': 28.1.1 '@types/node': 16.4.3 chalk: 4.1.1 - emittery: 0.8.1 - exit: 0.1.2 - graceful-fs: 4.2.6 - jest-docblock: 27.0.6 - jest-environment-jsdom: 27.0.6 - jest-environment-node: 27.0.6 - jest-haste-map: 27.0.6 - jest-leak-detector: 27.0.6 - jest-message-util: 27.0.6 - jest-resolve: 27.0.6 - jest-runtime: 27.0.6 - jest-util: 27.0.6 - jest-worker: 27.0.6 - source-map-support: 0.5.19 + emittery: 0.10.2 + graceful-fs: 4.2.10 + jest-docblock: 28.1.1 + jest-environment-node: 28.1.2 + jest-haste-map: 28.1.1 + jest-leak-detector: 28.1.1 + jest-message-util: 28.1.1 + jest-resolve: 28.1.1 + jest-runtime: 28.1.2 + jest-util: 28.1.1 + jest-watcher: 28.1.1 + jest-worker: 28.1.1 + source-map-support: 0.5.13 throat: 6.0.1 transitivePeerDependencies: - - bufferutil - - canvas - supports-color - - utf-8-validate - dev: true - - /jest-runtime/27.0.6: - resolution: {integrity: sha512-BhvHLRVfKibYyqqEFkybsznKwhrsu7AWx2F3y9G9L95VSIN3/ZZ9vBpm/XCS2bS+BWz3sSeNGLzI3TVQ0uL85Q==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@jest/console': 27.0.6 - '@jest/environment': 27.0.6 - '@jest/fake-timers': 27.0.6 - '@jest/globals': 27.0.6 - '@jest/source-map': 27.0.6 - '@jest/test-result': 27.0.6 - '@jest/transform': 27.0.6 - '@jest/types': 27.0.6 - '@types/yargs': 16.0.4 + dev: true + + /jest-runtime/28.1.2: + resolution: {integrity: sha512-i4w93OsWzLOeMXSi9epmakb2+3z0AchZtUQVF1hesBmcQQy4vtaql5YdVe9KexdJaVRyPDw8DoBR0j3lYsZVYw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/environment': 28.1.2 + '@jest/fake-timers': 28.1.2 + '@jest/globals': 28.1.2 + '@jest/source-map': 28.1.2 + '@jest/test-result': 28.1.1 + '@jest/transform': 28.1.2 + '@jest/types': 28.1.1 chalk: 4.1.1 cjs-module-lexer: 1.2.2 collect-v8-coverage: 1.0.1 - exit: 0.1.2 + execa: 5.1.1 glob: 7.1.7 - graceful-fs: 4.2.6 - jest-haste-map: 27.0.6 - jest-message-util: 27.0.6 - jest-mock: 27.0.6 - jest-regex-util: 27.0.6 - jest-resolve: 27.0.6 - jest-snapshot: 27.0.6 - jest-util: 27.0.6 - jest-validate: 27.0.6 + graceful-fs: 4.2.10 + jest-haste-map: 28.1.1 + jest-message-util: 28.1.1 + jest-mock: 28.1.1 + jest-regex-util: 28.0.2 + jest-resolve: 28.1.1 + jest-snapshot: 28.1.2 + jest-util: 28.1.1 slash: 3.0.0 strip-bom: 4.0.0 - yargs: 16.2.0 transitivePeerDependencies: - supports-color dev: true @@ -3496,44 +3622,35 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@types/node': 16.4.3 - graceful-fs: 4.2.6 - dev: true - - /jest-serializer/27.0.6: - resolution: {integrity: sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - dependencies: - '@types/node': 16.4.3 - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 dev: true - /jest-snapshot/27.0.6: - resolution: {integrity: sha512-NTHaz8He+ATUagUgE7C/UtFcRoHqR2Gc+KDfhQIyx+VFgwbeEMjeP+ILpUTLosZn/ZtbNdCF5LkVnN/l+V751A==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-snapshot/28.1.2: + resolution: {integrity: sha512-wzrieFttZYfLvrCVRJxX+jwML2YTArOUqFpCoSVy1QUapx+LlV9uLbV/mMEhYj4t7aMeE9aSQFHSvV/oNoDAMA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@babel/core': 7.14.8 '@babel/generator': 7.14.8 - '@babel/parser': 7.14.8 '@babel/plugin-syntax-typescript': 7.14.5_@babel+core@7.14.8 '@babel/traverse': 7.14.8 '@babel/types': 7.14.8 - '@jest/transform': 27.0.6 - '@jest/types': 27.0.6 + '@jest/expect-utils': 28.1.1 + '@jest/transform': 28.1.2 + '@jest/types': 28.1.1 '@types/babel__traverse': 7.14.2 '@types/prettier': 2.3.2 babel-preset-current-node-syntax: 1.0.1_@babel+core@7.14.8 chalk: 4.1.1 - expect: 27.0.6 - graceful-fs: 4.2.6 - jest-diff: 27.0.6 - jest-get-type: 27.0.6 - jest-haste-map: 27.0.6 - jest-matcher-utils: 27.0.6 - jest-message-util: 27.0.6 - jest-resolve: 27.0.6 - jest-util: 27.0.6 + expect: 28.1.1 + graceful-fs: 4.2.10 + jest-diff: 28.1.1 + jest-get-type: 28.0.2 + jest-haste-map: 28.1.1 + jest-matcher-utils: 28.1.1 + jest-message-util: 28.1.1 + jest-util: 28.1.1 natural-compare: 1.4.0 - pretty-format: 27.0.6 + pretty-format: 28.1.1 semver: 7.3.5 transitivePeerDependencies: - supports-color @@ -3546,45 +3663,46 @@ packages: '@jest/types': 26.6.2 '@types/node': 16.4.3 chalk: 4.1.1 - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 is-ci: 2.0.0 micromatch: 4.0.4 dev: true - /jest-util/27.0.6: - resolution: {integrity: sha512-1JjlaIh+C65H/F7D11GNkGDDZtDfMEM8EBXsvd+l/cxtgQ6QhxuloOaiayt89DxUvDarbVhqI98HhgrM1yliFQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-util/28.1.1: + resolution: {integrity: sha512-FktOu7ca1DZSyhPAxgxB6hfh2+9zMoJ7aEQA759Z6p45NuO8mWcqujH+UdHlCm/V6JTWwDztM2ITCzU1ijJAfw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 + '@jest/types': 28.1.1 '@types/node': 16.4.3 chalk: 4.1.1 - graceful-fs: 4.2.6 - is-ci: 3.0.0 + ci-info: 3.2.0 + graceful-fs: 4.2.10 picomatch: 2.3.0 dev: true - /jest-validate/27.0.6: - resolution: {integrity: sha512-yhZZOaMH3Zg6DC83n60pLmdU1DQE46DW+KLozPiPbSbPhlXXaiUTDlhHQhHFpaqIFRrInko1FHXjTRpjWRuWfA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-validate/28.1.1: + resolution: {integrity: sha512-Kpf6gcClqFCIZ4ti5++XemYJWUPCFUW+N2gknn+KgnDf549iLul3cBuKVe1YcWRlaF8tZV8eJCap0eECOEE3Ug==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 + '@jest/types': 28.1.1 camelcase: 6.2.0 chalk: 4.1.1 - jest-get-type: 27.0.6 + jest-get-type: 28.0.2 leven: 3.1.0 - pretty-format: 27.0.6 + pretty-format: 28.1.1 dev: true - /jest-watcher/27.0.6: - resolution: {integrity: sha512-/jIoKBhAP00/iMGnTwUBLgvxkn7vsOweDrOTSPzc7X9uOyUtJIDthQBTI1EXz90bdkrxorUZVhJwiB69gcHtYQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-watcher/28.1.1: + resolution: {integrity: sha512-RQIpeZ8EIJMxbQrXpJQYIIlubBnB9imEHsxxE41f54ZwcqWLysL/A0ZcdMirf+XsMn3xfphVQVV4EW0/p7i7Ug==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/test-result': 27.0.6 - '@jest/types': 27.0.6 + '@jest/test-result': 28.1.1 + '@jest/types': 28.1.1 '@types/node': 16.4.3 ansi-escapes: 4.3.2 chalk: 4.1.1 - jest-util: 27.0.6 + emittery: 0.10.2 + jest-util: 28.1.1 string-length: 4.0.2 dev: true @@ -3597,18 +3715,18 @@ packages: supports-color: 7.2.0 dev: true - /jest-worker/27.0.6: - resolution: {integrity: sha512-qupxcj/dRuA3xHPMUd40gr2EaAurFbkwzOh7wfPaeE9id7hyjURRQoqNfHifHK3XjJU6YJJUQKILGUnwGPEOCA==} - engines: {node: '>= 10.13.0'} + /jest-worker/28.1.1: + resolution: {integrity: sha512-Au7slXB08C6h+xbJPp7VIb6U0XX5Kc9uel/WFc6/rcTzGiaVCBRngBExSYuXSLFPULPSYU3cJ3ybS988lNFQhQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: '@types/node': 16.4.3 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest/27.0.6: - resolution: {integrity: sha512-EjV8aETrsD0wHl7CKMibKwQNQc3gIRBXlTikBmmHUeVMKaPFxdcUIBfoDqTSXDoGJIivAYGqCWVlzCSaVjPQsA==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest/28.1.2_@types+node@16.4.3: + resolution: {integrity: sha512-Tuf05DwLeCh2cfWCQbcz9UxldoDyiR1E9Igaei5khjonKncYdc6LDfynKCEWozK0oLE3GD+xKAo2u8x/0s6GOg==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -3616,15 +3734,14 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 27.0.6 + '@jest/core': 28.1.2 + '@jest/types': 28.1.1 import-local: 3.0.2 - jest-cli: 27.0.6 + jest-cli: 28.1.2_@types+node@16.4.3 transitivePeerDependencies: - - bufferutil - - canvas + - '@types/node' - supports-color - ts-node - - utf-8-validate dev: true /js-tokens/4.0.0: @@ -3639,48 +3756,6 @@ packages: esprima: 4.0.1 dev: true - /jsdom/16.6.0: - resolution: {integrity: sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg==} - engines: {node: '>=10'} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - dependencies: - abab: 2.0.5 - acorn: 8.4.1 - acorn-globals: 6.0.0 - cssom: 0.4.4 - cssstyle: 2.3.0 - data-urls: 2.0.0 - decimal.js: 10.3.1 - domexception: 2.0.1 - escodegen: 2.0.0 - form-data: 3.0.1 - html-encoding-sniffer: 2.0.1 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.0 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.0 - parse5: 6.0.1 - saxes: 5.0.1 - symbol-tree: 3.2.4 - tough-cookie: 4.0.0 - w3c-hr-time: 1.0.2 - w3c-xmlserializer: 2.0.0 - webidl-conversions: 6.1.0 - whatwg-encoding: 1.0.5 - whatwg-mimetype: 2.3.0 - whatwg-url: 8.7.0 - ws: 7.5.3 - xml-name-validator: 3.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - /jsesc/2.5.2: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} @@ -3720,7 +3795,7 @@ packages: dependencies: universalify: 2.0.0 optionalDependencies: - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 dev: false /jsx-ast-utils/3.2.0: @@ -3732,14 +3807,14 @@ packages: dev: true /kind-of/3.2.2: - resolution: {integrity: sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=} + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} engines: {node: '>=0.10.0'} dependencies: is-buffer: 1.1.6 dev: true /kind-of/4.0.0: - resolution: {integrity: sha1-IIE989cSkosgc3hpGkUGb65y3Vc=} + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} engines: {node: '>=0.10.0'} dependencies: is-buffer: 1.1.6 @@ -3775,14 +3850,6 @@ packages: engines: {node: '>=6'} dev: true - /levn/0.3.0: - resolution: {integrity: sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.1.2 - type-check: 0.3.2 - dev: true - /levn/0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -3799,7 +3866,7 @@ packages: resolution: {integrity: sha1-L19Fq5HjMhYjT9U62rZo607AmTs=} engines: {node: '>=4'} dependencies: - graceful-fs: 4.2.6 + graceful-fs: 4.2.10 parse-json: 4.0.0 pify: 3.0.0 strip-bom: 3.0.0 @@ -3850,6 +3917,26 @@ packages: yallist: 4.0.0 dev: true + /luabundle/1.6.0: + resolution: {integrity: sha512-WAiumy/R8GZIq4eCkFkOYXjAqxZUphGrKVvr5AG/obflBgV8ltiNLMF8nKbCmZvhR0b94S4rYFKisJ5eYJaRvA==} + engines: {node: '>=8.0.0'} + dependencies: + moonsharp-luaparse: 0.2.4 + tslib: 1.14.1 + dev: false + + /luamin/1.0.4: + resolution: {integrity: sha512-z1h0bclRD/QGsS/Dz4Skp9z0qPTmmm+IKcrCapGmdTczPWVdN9f9jk6WqkNrcifQr8+n9Pzsm9WkwpOH1JBUAQ==} + hasBin: true + dependencies: + luaparse: 0.2.1 + dev: false + + /luaparse/0.2.1: + resolution: {integrity: sha512-VKBcryd5nJte4ZNR29NOk8F/UkMipjeb4yoxcSS51z6QAzg9DXUC2WsfLniS0J1eh3pr/ZL3e9ha6V8fhoLbBQ==} + hasBin: true + dev: false + /make-dir/3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -3857,19 +3944,19 @@ packages: semver: 6.3.0 dev: true - /makeerror/1.0.11: - resolution: {integrity: sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=} + /makeerror/1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} dependencies: - tmpl: 1.0.4 + tmpl: 1.0.5 dev: true /map-cache/0.2.2: - resolution: {integrity: sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=} + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} engines: {node: '>=0.10.0'} dev: true /map-visit/1.0.0: - resolution: {integrity: sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=} + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} engines: {node: '>=0.10.0'} dependencies: object-visit: 1.0.1 @@ -3901,6 +3988,8 @@ packages: regex-not: 1.0.2 snapdragon: 0.8.2 to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color dev: true /micromatch/4.0.4: @@ -3911,18 +4000,6 @@ packages: picomatch: 2.3.0 dev: true - /mime-db/1.48.0: - resolution: {integrity: sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==} - engines: {node: '>= 0.6'} - dev: true - - /mime-types/2.1.31: - resolution: {integrity: sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.48.0 - dev: true - /mimic-fn/2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -3946,6 +4023,11 @@ packages: is-extendable: 1.0.1 dev: true + /moonsharp-luaparse/0.2.4: + resolution: {integrity: sha512-1bkZPHRVJhCzt681KnFxWB5fYQlmcOZuBAlzdcsdTSjDfL//sqjcH2L2bbbgxf/l5s1krpGL8Ba7twT7mdj91Q==} + hasBin: true + dev: false + /ms/2.0.0: resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} dev: true @@ -3982,6 +4064,8 @@ packages: regex-not: 1.0.2 snapdragon: 0.8.2 to-regex: 3.0.2 + transitivePeerDependencies: + - supports-color dev: true /natural-compare/1.4.0: @@ -3993,11 +4077,11 @@ packages: dev: true /node-int64/0.4.0: - resolution: {integrity: sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=} + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true /node-modules-regexp/1.0.0: - resolution: {integrity: sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=} + resolution: {integrity: sha512-JMaRS9L4wSRIR+6PTVEikTrq/lMGEZR43a48ETeilY0Q0iMwVnccMFrUM1k+tNzmYuIU0Vh710bCUqHX+/+ctQ==} engines: {node: '>=0.10.0'} dev: true @@ -4015,7 +4099,7 @@ packages: dev: true /normalize-path/2.1.1: - resolution: {integrity: sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=} + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} engines: {node: '>=0.10.0'} dependencies: remove-trailing-separator: 1.1.0 @@ -4027,7 +4111,7 @@ packages: dev: true /npm-run-path/2.0.2: - resolution: {integrity: sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=} + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} engines: {node: '>=4'} dependencies: path-key: 2.0.1 @@ -4040,17 +4124,13 @@ packages: path-key: 3.1.1 dev: true - /nwsapi/2.2.0: - resolution: {integrity: sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==} - dev: true - /object-assign/4.1.1: resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=} engines: {node: '>=0.10.0'} dev: true /object-copy/0.1.0: - resolution: {integrity: sha1-fn2Fi3gb18mRpBupde04EnVOmYw=} + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} engines: {node: '>=0.10.0'} dependencies: copy-descriptor: 0.1.1 @@ -4068,7 +4148,7 @@ packages: dev: true /object-visit/1.0.1: - resolution: {integrity: sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=} + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} engines: {node: '>=0.10.0'} dependencies: isobject: 3.0.1 @@ -4104,7 +4184,7 @@ packages: dev: true /object.pick/1.3.0: - resolution: {integrity: sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=} + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} engines: {node: '>=0.10.0'} dependencies: isobject: 3.0.1 @@ -4132,18 +4212,6 @@ packages: mimic-fn: 2.1.0 dev: true - /optionator/0.8.3: - resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.3 - fast-levenshtein: 2.0.6 - levn: 0.3.0 - prelude-ls: 1.1.2 - type-check: 0.3.2 - word-wrap: 1.2.3 - dev: true - /optionator/0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} @@ -4156,13 +4224,8 @@ packages: word-wrap: 1.2.3 dev: true - /p-each-series/2.2.0: - resolution: {integrity: sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==} - engines: {node: '>=8'} - dev: true - /p-finally/1.0.0: - resolution: {integrity: sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=} + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} dev: true @@ -4229,12 +4292,8 @@ packages: lines-and-columns: 1.1.6 dev: true - /parse5/6.0.1: - resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - dev: true - /pascalcase/0.1.1: - resolution: {integrity: sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=} + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} engines: {node: '>=0.10.0'} dev: true @@ -4254,7 +4313,7 @@ packages: dev: true /path-key/2.0.1: - resolution: {integrity: sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=} + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} engines: {node: '>=4'} dev: true @@ -4296,6 +4355,11 @@ packages: node-modules-regexp: 1.0.0 dev: true + /pirates/4.0.5: + resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} + engines: {node: '>= 6'} + dev: true + /pkg-dir/2.0.0: resolution: {integrity: sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=} engines: {node: '>=4'} @@ -4323,15 +4387,10 @@ packages: dev: true /posix-character-classes/0.1.1: - resolution: {integrity: sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=} + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} engines: {node: '>=0.10.0'} dev: true - /prelude-ls/1.1.2: - resolution: {integrity: sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=} - engines: {node: '>= 0.8.0'} - dev: true - /prelude-ls/1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -4360,14 +4419,14 @@ packages: react-is: 17.0.2 dev: true - /pretty-format/27.0.6: - resolution: {integrity: sha512-8tGD7gBIENgzqA+UBzObyWqQ5B778VIFZA/S66cclyd5YkFLYs2Js7gxDKf0MXtTc9zcS7t1xhdfcElJ3YIvkQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /pretty-format/28.1.1: + resolution: {integrity: sha512-wwJbVTGFHeucr5Jw2bQ9P+VYHyLdAqedFLEkdQUVaBF/eiidDwH5OpilINq4mEfhbCjLnirt6HTTDhv1HaTIQw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dependencies: - '@jest/types': 27.0.6 - ansi-regex: 5.0.0 + '@jest/schemas': 28.0.2 + ansi-regex: 5.0.1 ansi-styles: 5.2.0 - react-is: 17.0.2 + react-is: 18.2.0 dev: true /progress/2.0.3: @@ -4391,10 +4450,6 @@ packages: react-is: 16.13.1 dev: true - /psl/1.8.0: - resolution: {integrity: sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==} - dev: true - /pump/3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} dependencies: @@ -4419,6 +4474,10 @@ packages: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true + /react-is/18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + /read-pkg-up/3.0.0: resolution: {integrity: sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=} engines: {node: '>=4'} @@ -4486,7 +4545,7 @@ packages: dev: true /remove-trailing-separator/1.1.0: - resolution: {integrity: sha1-wkvOKig62tW8P1jg1IJJuSN52O8=} + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} dev: true /repeat-element/1.1.4: @@ -4495,12 +4554,12 @@ packages: dev: true /repeat-string/1.6.1: - resolution: {integrity: sha1-jcrkcOHIirwtYA//Sndihtp15jc=} + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} engines: {node: '>=0.10'} dev: true /require-directory/2.1.1: - resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} dev: true @@ -4531,10 +4590,15 @@ packages: dev: true /resolve-url/0.2.1: - resolution: {integrity: sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=} + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} deprecated: https://github.com/lydell/resolve-url#deprecated dev: true + /resolve.exports/1.1.0: + resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} + engines: {node: '>=10'} + dev: true + /resolve/1.20.0: resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==} dependencies: @@ -4582,7 +4646,7 @@ packages: dev: true /safe-regex/1.1.0: - resolution: {integrity: sha1-QKNmnzsHfR6UPURinhV91IAjvy4=} + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} dependencies: ret: 0.1.15 dev: true @@ -4593,10 +4657,6 @@ packages: regexp-tree: 0.1.23 dev: true - /safer-buffer/2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true - /sane/4.1.0: resolution: {integrity: sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==} engines: {node: 6.* || 8.* || >= 10.*} @@ -4611,14 +4671,9 @@ packages: fb-watchman: 2.0.1 micromatch: 3.1.10 minimist: 1.2.5 - walker: 1.0.7 - dev: true - - /saxes/5.0.1: - resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} - engines: {node: '>=10'} - dependencies: - xmlchars: 2.2.0 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color dev: true /semver/5.7.1: @@ -4650,7 +4705,7 @@ packages: dev: true /shebang-command/1.2.0: - resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=} + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} dependencies: shebang-regex: 1.0.0 @@ -4664,7 +4719,7 @@ packages: dev: true /shebang-regex/1.0.0: - resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=} + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} engines: {node: '>=0.10.0'} dev: true @@ -4685,6 +4740,10 @@ packages: resolution: {integrity: sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==} dev: true + /signal-exit/3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + /sisteransi/1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true @@ -4731,10 +4790,13 @@ packages: source-map: 0.5.7 source-map-resolve: 0.5.3 use: 3.1.1 + transitivePeerDependencies: + - supports-color dev: true /source-map-resolve/0.5.3: resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated dependencies: atob: 2.1.2 decode-uri-component: 0.2.0 @@ -4743,8 +4805,8 @@ packages: urix: 0.1.0 dev: true - /source-map-support/0.5.19: - resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==} + /source-map-support/0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: buffer-from: 1.1.1 source-map: 0.6.1 @@ -4752,10 +4814,11 @@ packages: /source-map-url/0.4.1: resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated dev: true /source-map/0.5.7: - resolution: {integrity: sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=} + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} engines: {node: '>=0.10.0'} dev: true @@ -4764,11 +4827,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /source-map/0.7.3: - resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==} - engines: {node: '>= 8'} - dev: true - /spdx-correct/3.1.1: resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} dependencies: @@ -4810,7 +4868,7 @@ packages: dev: true /static-extend/0.1.2: - resolution: {integrity: sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=} + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} engines: {node: '>=0.10.0'} dependencies: define-property: 0.2.5 @@ -4834,6 +4892,15 @@ packages: strip-ansi: 6.0.0 dev: true + /string-width/4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + /string.prototype.matchall/4.0.5: resolution: {integrity: sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==} dependencies: @@ -4868,6 +4935,13 @@ packages: ansi-regex: 5.0.0 dev: true + /strip-ansi/6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + /strip-bom/3.0.0: resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} engines: {node: '>=4'} @@ -4879,7 +4953,7 @@ packages: dev: true /strip-eof/1.0.0: - resolution: {integrity: sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=} + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} engines: {node: '>=0.10.0'} dev: true @@ -4922,10 +4996,6 @@ packages: supports-color: 7.2.0 dev: true - /symbol-tree/3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - dev: true - /table/6.7.1: resolution: {integrity: sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==} engines: {node: '>=10.0.0'} @@ -4963,24 +5033,24 @@ packages: resolution: {integrity: sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==} dev: true - /tmpl/1.0.4: - resolution: {integrity: sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=} + /tmpl/1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} dev: true /to-fast-properties/2.0.0: - resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=} + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} dev: true /to-object-path/0.3.0: - resolution: {integrity: sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=} + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} engines: {node: '>=0.10.0'} dependencies: kind-of: 3.2.2 dev: true /to-regex-range/2.1.1: - resolution: {integrity: sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=} + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} engines: {node: '>=0.10.0'} dependencies: is-number: 3.0.0 @@ -5004,22 +5074,6 @@ packages: safe-regex: 1.1.0 dev: true - /tough-cookie/4.0.0: - resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} - engines: {node: '>=6'} - dependencies: - psl: 1.8.0 - punycode: 2.1.1 - universalify: 0.1.2 - dev: true - - /tr46/2.1.0: - resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} - engines: {node: '>=8'} - dependencies: - punycode: 2.1.1 - dev: true - /tsconfig-paths/3.10.1: resolution: {integrity: sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==} dependencies: @@ -5030,23 +5084,15 @@ packages: /tslib/1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true - /tsutils/3.21.0_typescript@4.3.5: + /tsutils/3.21.0_typescript@4.7.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 4.3.5 - dev: true - - /type-check/0.3.2: - resolution: {integrity: sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.1.2 + typescript: 4.7.4 dev: true /type-check/0.4.0: @@ -5087,8 +5133,8 @@ packages: is-typedarray: 1.0.0 dev: true - /typescript/4.3.5: - resolution: {integrity: sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==} + /typescript/4.7.4: + resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} engines: {node: '>=4.2.0'} hasBin: true dev: true @@ -5112,18 +5158,13 @@ packages: set-value: 2.0.1 dev: true - /universalify/0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true - /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} dev: false /unset-value/1.0.0: - resolution: {integrity: sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=} + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} engines: {node: '>=0.10.0'} dependencies: has-value: 0.3.1 @@ -5137,7 +5178,7 @@ packages: dev: true /urix/0.1.0: - resolution: {integrity: sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=} + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} deprecated: Please see https://github.com/lydell/urix#deprecated dev: true @@ -5150,13 +5191,13 @@ packages: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: true - /v8-to-istanbul/8.0.0: - resolution: {integrity: sha512-LkmXi8UUNxnCC+JlH7/fsfsKr5AU110l+SYGJimWNkWhxbN5EyeOtm1MJ0hhvqMMOhGwBj1Fp70Yv9i+hX0QAg==} + /v8-to-istanbul/9.0.1: + resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} engines: {node: '>=10.12.0'} dependencies: + '@jridgewell/trace-mapping': 0.3.14 '@types/istanbul-lib-coverage': 2.0.3 convert-source-map: 1.8.0 - source-map: 0.7.3 dev: true /validate-npm-package-license/3.0.4: @@ -5166,52 +5207,10 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /w3c-hr-time/1.0.2: - resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} - dependencies: - browser-process-hrtime: 1.0.0 - dev: true - - /w3c-xmlserializer/2.0.0: - resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==} - engines: {node: '>=10'} - dependencies: - xml-name-validator: 3.0.0 - dev: true - - /walker/1.0.7: - resolution: {integrity: sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=} + /walker/1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: - makeerror: 1.0.11 - dev: true - - /webidl-conversions/5.0.0: - resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} - engines: {node: '>=8'} - dev: true - - /webidl-conversions/6.1.0: - resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==} - engines: {node: '>=10.4'} - dev: true - - /whatwg-encoding/1.0.5: - resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} - dependencies: - iconv-lite: 0.4.24 - dev: true - - /whatwg-mimetype/2.3.0: - resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} - dev: true - - /whatwg-url/8.7.0: - resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==} - engines: {node: '>=10'} - dependencies: - lodash: 4.17.21 - tr46: 2.1.0 - webidl-conversions: 6.1.0 + makeerror: 1.0.12 dev: true /which-boxed-primitive/1.0.2: @@ -5249,8 +5248,8 @@ packages: engines: {node: '>=10'} dependencies: ansi-styles: 4.3.0 - string-width: 4.2.2 - strip-ansi: 6.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 dev: true /wrappy/1.0.2: @@ -5266,25 +5265,12 @@ packages: typedarray-to-buffer: 3.1.5 dev: true - /ws/7.5.3: - resolution: {integrity: sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: true - - /xml-name-validator/3.0.0: - resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==} - dev: true - - /xmlchars/2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + /write-file-atomic/4.0.1: + resolution: {integrity: sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 dev: true /y18n/5.0.8: @@ -5296,20 +5282,20 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yargs-parser/20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} + /yargs-parser/21.0.1: + resolution: {integrity: sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==} + engines: {node: '>=12'} dev: true - /yargs/16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} + /yargs/17.5.1: + resolution: {integrity: sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==} + engines: {node: '>=12'} dependencies: cliui: 7.0.4 escalade: 3.1.1 get-caller-file: 2.0.5 require-directory: 2.1.1 - string-width: 4.2.2 + string-width: 4.2.3 y18n: 5.0.8 - yargs-parser: 20.2.9 + yargs-parser: 21.0.1 dev: true diff --git a/.github/actions/bundle/src/bundle.test.ts b/.github/actions/bundle/src/bundle.test.ts deleted file mode 100644 index 2d5e55eb..00000000 --- a/.github/actions/bundle/src/bundle.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { Library } from './bundle' -import { bundleFile } from './bundle' - -const library: Library = { - articulation: { - contents: `--[[ -$module Articulation -]] - -local BUNDLED_LIBRARY_VARIABLE_NAME = {} - -function BUNDLED_LIBRARY_VARIABLE_NAME.method() -end`, - }, - expression: { - contents: `--[[ -$module Articulation -]] - -local BUNDLED_LIBRARY_VARIABLE_NAME = {} - -function BUNDLED_LIBRARY_VARIABLE_NAME.method() - -- does something -end`, - }, -} - -it('bundles library if library exists', () => { - const file = ` -local library = require("library.articulation") -` - - const output = ` ---[[ -$module Articulation -]] - -local library = {} - -function library.method() -end -` - expect(bundleFile(file, library)).toEqual(output) -}) - -it('works with multiple imports', () => { - const file = ` -local articulation = require("library.articulation") -local expression = require("library.expression") -` - - const output = ` ---[[ -$module Articulation -]] - -local articulation = {} - -function articulation.method() -end ---[[ -$module Articulation -]] - -local expression = {} - -function expression.method() - -- does something -end -` - expect(bundleFile(file, library)).toEqual(output) -}) diff --git a/.github/actions/bundle/src/bundle.ts b/.github/actions/bundle/src/bundle.ts deleted file mode 100644 index 294e445a..00000000 --- a/.github/actions/bundle/src/bundle.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { getImport, getVariableName } from './helpers' - -export type Library = { - [name: string]: { - contents: string - } -} - -export const bundleFile = (file: string, library: Library): string => { - const lines = file.split('\n') - const output: string[] = [] - lines.forEach((line) => { - const { importedFile, isImport } = getImport(line) - if (!isImport && !(importedFile in library)) { - output.push(line) - return - } - const variableName = getVariableName(line) - output.push( - library[importedFile].contents.replace(/BUNDLED_LIBRARY_VARIABLE_NAME/gu, variableName) - ) - }) - return output.join('\n') -} diff --git a/.github/actions/bundle/src/helpers.test.ts b/.github/actions/bundle/src/helpers.test.ts deleted file mode 100644 index 6e7b07b9..00000000 --- a/.github/actions/bundle/src/helpers.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { getImport, getVariableName, getAllImports } from './helpers' - -describe('detects valid imports', () => { - const lines: [string, string][] = [ - ['local library = require("library.file_name")', 'file_name'], - ['local library = require("Library.file_name")', 'file_name'], - ['local library = require("library.articulation")', 'articulation'], - ['local library = require("library.Articulation")', 'Articulation'], - ['local library = require("library.articulation") -- no path, no ".lua"', 'articulation'], - ['library = require("library.articulation")', 'articulation'], - ['articulation = require("library.articulation")', 'articulation'], - ['articulation = require("library.file_name")', 'file_name'], - ["articulation = require('library.file_name')", 'file_name'], - ['local library = require("not_library.file_name")', ''], - ['local library = import("library.file_name")', ''], - ['local library = require("library.")', ''], - ['local library = require("file_name")', ''], - ] - - it.each(lines)('line "%s" imports "%s"', (line, importFile) => { - const { importedFile, isImport } = getImport(line) - expect(importedFile).toEqual(importFile) - expect(isImport).toEqual(importFile !== '') - }) -}) - -describe('gets the defined variable name', () => { - const lines: [string, string][] = [ - ['local library = require("library.file_name")', 'library'], - ['local library = require("library.articulation") -- no path, no ".lua"', 'library'], - ['library = require("library.articulation")', 'library'], - ['articulation = require("library.articulation")', 'articulation'], - ['articulation = require("library.file_name")', 'articulation'], - ["articulation = require('library.file_name')", 'articulation'], - ] - - it.each(lines)('line "%s" imports "%s"', (line, variableName) => { - expect(getVariableName(line)).toEqual(variableName) - }) -}) - -describe('checks if file imports anything', () => { - const lines: [string, Set][] = [ - ['local library = require("library.file_name")', new Set(['file_name'])], - ['this is some random text', new Set()], - [ - 'local library = require("library.expression")\nlocal library = require("library.articulation")', - new Set(['expression', 'articulation']), - ], - ] - - it.each(lines)('line "%s" imports "%s"', (file, imports) => { - expect(getAllImports(file)).toEqual(imports) - }) -}) diff --git a/.github/actions/bundle/src/helpers.ts b/.github/actions/bundle/src/helpers.ts deleted file mode 100644 index b3ff56c1..00000000 --- a/.github/actions/bundle/src/helpers.ts +++ /dev/null @@ -1,21 +0,0 @@ -export const getImport = (line: string): { importedFile: string; isImport: boolean } => { - const matches = line.match(/require\(["']library\.([A-Z_a-z]+)["']\)/iu) - if (!matches) return { importedFile: '', isImport: false } - return { importedFile: matches[1], isImport: true } -} - -export const getVariableName = (line: string): string => { - const matches = line.match(/^(local )?([a-zA-Z_]+)/u) - if (!matches) return '' - return matches[2] -} - -export const getAllImports = (file: string): Set => { - const imports: Set = new Set() - const lines = file.split('\n') - for (const line of lines) { - const { isImport, importedFile } = getImport(line) - if (isImport) imports.add(importedFile) - } - return imports -} diff --git a/.github/actions/bundle/src/index.ts b/.github/actions/bundle/src/index.ts index a1e67363..df066bd0 100644 --- a/.github/actions/bundle/src/index.ts +++ b/.github/actions/bundle/src/index.ts @@ -2,49 +2,39 @@ import path from 'path' import { getInput } from '@actions/core' import fs from 'fs-extra' +import luaBundle from 'luabundle' +import luaMinify from 'luamin' -import { bundleFile } from './bundle' -import type { LibraryInput } from './prepare-library' -import { prepareLibrary } from './prepare-library' +const IS_DEV_ENVIRONMENT = process.env.NODE_ENV === 'development' +const sourcePath = IS_DEV_ENVIRONMENT + ? path.join('..', '..', '..', 'src') + : path.join(...getInput('source', { required: true }).split('/')) -const sourcePath = path.join(...getInput('source', { required: true }).split('/')) -const libraryPath = path.join(sourcePath, 'library') +const outputPath = IS_DEV_ENVIRONMENT + ? path.join('..', '..', '..', 'dist') + : path.join(...getInput('output', { required: true }).split('/')) -const outputPath = path.join(...getInput('output', { required: true }).split('/')) - -/* - create bundled library files - */ - -const libraryFileNames = fs.readdirSync(libraryPath) -const libraryRawFiles: LibraryInput = [] - -libraryFileNames.forEach((fileName) => { - const name = fileName.replace('.lua', '') - const contents = fs.readFileSync(path.join(libraryPath, fileName)).toString() - libraryRawFiles.push({ - fileName: name, - contents, - }) -}) - -const library = prepareLibrary(libraryRawFiles) - -/* +/* remove old bundled files (if they exist) */ fs.ensureDirSync(outputPath) -fs.readdirSync(outputPath).forEach((fileName) => fs.removeSync(fileName)) +fs.readdirSync(outputPath).forEach(fileName => fs.removeSync(fileName)) -/* +/* bundle and save source files */ -const sourceFiles = fs.readdirSync(sourcePath).filter((fileName) => fileName.endsWith('.lua')) +const sourceFiles = fs.readdirSync(sourcePath).filter(fileName => fileName.endsWith('.lua')) +const mixins = fs.readdirSync(path.join(sourcePath, 'mixin')).filter(fileName => fileName.endsWith('.lua')) -sourceFiles.forEach((file) => { - const contents = fs.readFileSync(path.join(sourcePath, file)).toString() - const bundledFile = bundleFile(contents, library) - fs.writeFileSync(path.join(outputPath, file), bundledFile) +sourceFiles.forEach(file => { + if (file.startsWith('personal')) return + const source = fs.readFileSync(path.join(sourcePath, file), 'utf8') + let expressionHandlerOutput = source.includes('library.mixin') ? mixins : [] + const bundledFile = luaBundle.bundle(path.join(sourcePath, file), { + paths: ['?', '?.lua', path.resolve(path.join(sourcePath, 'library', 'general_library.lua'))], + expressionHandler: (_, __) => expressionHandlerOutput, + }) + fs.writeFileSync(path.join(outputPath, file), luaMinify.minify(bundledFile)) }) diff --git a/.github/actions/bundle/src/prepare-library.test.ts b/.github/actions/bundle/src/prepare-library.test.ts deleted file mode 100644 index d22ad979..00000000 --- a/.github/actions/bundle/src/prepare-library.test.ts +++ /dev/null @@ -1,155 +0,0 @@ -import type { LibraryInput } from './prepare-library' -import { prepareLibraryFile, LIBRARY_VARIABLE_NAME, prepareLibrary } from './prepare-library' - -const articulationFile = ` ---[[ -$module Articulation -]] - -local articulation = {} - ---[[ -% delete_from_entry_by_char_num(entry, char_num) -Removes any articulation assignment that has the specified character as its above-character. -@ entry (FCNoteEntry) -@ char_num (number) UTF-32 code of character (which is the same as ASCII for ASCII characters) -]] -function articulation.delete_from_entry_by_char_num(entry, char_num) - local artics = entry:CreateArticulations() - for a in eachbackwards(artics) do - local defs = a:CreateArticulationDef() - if defs:GetAboveSymbolChar() == char_num then - a:DeleteData() - end - end -end - -return articulation` - -const preparedArticulationFile = ` ---[[ -$module Articulation -]] - -local ${LIBRARY_VARIABLE_NAME} = {} - ---[[ -% delete_from_entry_by_char_num(entry, char_num) -Removes any articulation assignment that has the specified character as its above-character. -@ entry (FCNoteEntry) -@ char_num (number) UTF-32 code of character (which is the same as ASCII for ASCII characters) -]] -function ${LIBRARY_VARIABLE_NAME}.delete_from_entry_by_char_num(entry, char_num) - local artics = entry:CreateArticulations() - for a in eachbackwards(artics) do - local defs = a:CreateArticulationDef() - if defs:GetAboveSymbolChar() == char_num then - a:DeleteData() - end - end -end - -` - -const bundledArticulationFile = preparedArticulationFile - -const expressionFile = ` ---[[ -$module Articulation -]] - -local expression = {} - -local articulation = require("library.articulation") - ---[[ -% delete_from_entry_by_char_num(entry, char_num) -Removes any articulation assignment that has the specified character as its above-character. -@ entry (FCNoteEntry) -@ char_num (number) UTF-32 code of character (which is the same as ASCII for ASCII characters) -]] -function expression.delete_from_entry_by_char_num(entry, char_num) - local artics = entry:CreateArticulations() - for a in eachbackwards(artics) do - local defs = a:CreateArticulationDef() - if defs:GetAboveSymbolChar() == char_num then - a:DeleteData() - end - end -end - -return expression` - -const bundledExpressionFile = ` ---[[ -$module Articulation -]] - -local ${LIBRARY_VARIABLE_NAME} = {} - -${bundledArticulationFile.replace(new RegExp(LIBRARY_VARIABLE_NAME, 'gu'), 'articulation')} - ---[[ -% delete_from_entry_by_char_num(entry, char_num) -Removes any articulation assignment that has the specified character as its above-character. -@ entry (FCNoteEntry) -@ char_num (number) UTF-32 code of character (which is the same as ASCII for ASCII characters) -]] -function ${LIBRARY_VARIABLE_NAME}.delete_from_entry_by_char_num(entry, char_num) - local artics = entry:CreateArticulations() - for a in eachbackwards(artics) do - local defs = a:CreateArticulationDef() - if defs:GetAboveSymbolChar() == char_num then - a:DeleteData() - end - end -end - -` - -it('empty file stays empty', () => { - const file = '' - const { contents } = prepareLibraryFile(file, 'articulation') - expect(contents).toBe('') -}) - -it('file name is replaced and remove the return statement', () => { - const file = articulationFile - const outputContents = preparedArticulationFile - const { contents } = prepareLibraryFile(file, 'articulation') - expect(contents).toBe(outputContents) -}) - -it('if it is not a library file, return the original contents', () => { - const file = `hello world` - - const outputContents = `hello world` - const { contents } = prepareLibraryFile(file, 'articulation') - expect(contents).toBe(outputContents) -}) - -it('base case -- no library files', () => { - const files: LibraryInput = [] - expect(prepareLibrary(files)).toStrictEqual({}) -}) - -it('prepares all library files', () => { - const files: LibraryInput = [ - { - fileName: 'articulation', - contents: articulationFile, - }, - { - fileName: 'expression', - contents: expressionFile, - }, - ] - expect(prepareLibrary(files)).toStrictEqual({ - articulation: { - contents: bundledArticulationFile, - }, - expression: { - contents: bundledExpressionFile, - }, - }) -}) diff --git a/.github/actions/bundle/src/prepare-library.ts b/.github/actions/bundle/src/prepare-library.ts deleted file mode 100644 index c50da5cc..00000000 --- a/.github/actions/bundle/src/prepare-library.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { Library } from './bundle' -import { bundleFile } from './bundle' -import { getAllImports } from './helpers' - -export const LIBRARY_VARIABLE_NAME = 'BUNDLED_LIBRARY_VARIABLE_NAME' - -export const prepareLibraryFile = (file: string, fileName: string): { contents: string } => { - const lines = file.split('\n') - let internalModuleName = fileName - if (!internalModuleName.match(new RegExp(`local ${internalModuleName} = \\{\\}`, 'u'))) { - for (const line of lines) { - const matches = line.match(/^local ([a-zA-Z+]+) = \{\}/iu) - if (matches?.[1]) { - internalModuleName = matches[1] - break - } - } - } - const output: string[] = [] - const declarationRegex = new RegExp(`^local ${internalModuleName} = \\{\\}`, 'u') - const methodRegex = new RegExp(`^function ${internalModuleName}\\.`, 'ug') - const returnRegex = new RegExp(`^return ${internalModuleName}`, 'ug') - - for (const line of lines) { - let outputtedLine = line.replace(declarationRegex, `local ${LIBRARY_VARIABLE_NAME} = {}`) - outputtedLine = outputtedLine.replace(methodRegex, `function ${LIBRARY_VARIABLE_NAME}.`) - outputtedLine = outputtedLine.replace(returnRegex, ``) - output.push(outputtedLine) - } - return { contents: output.join('\n') } -} - -export type LibraryInput = { - fileName: string - contents: string -}[] - -/* eslint-disable-next-line sonarjs/cognitive-complexity -- fix in the future */ -export const prepareLibrary = (inputFiles: LibraryInput): Library => { - let files = [...inputFiles] - const library: Library = {} - let counter = 0 - - while (files.length > 0 && counter < 10) { - const completedFiles: Set = new Set() - for (const file of files) { - const imports = getAllImports(file.contents) - for (const importFile of imports) if (importFile in library) imports.delete(importFile) - - /* eslint-disable-next-line no-continue -- it's just best here */ - if (imports.size > 0) continue - - const preparedFile = prepareLibraryFile(file.contents, file.fileName) - const bundledFile = bundleFile(preparedFile.contents, library) - completedFiles.add(file.fileName) - library[file.fileName] = { contents: bundledFile } - } - files = files.filter((file) => !completedFiles.has(file.fileName)) - counter++ - } - return library -} diff --git a/.github/actions/bundle/types.d.ts b/.github/actions/bundle/types.d.ts new file mode 100644 index 00000000..2158845b --- /dev/null +++ b/.github/actions/bundle/types.d.ts @@ -0,0 +1,3 @@ +declare module 'luamin' { + export function minify(code: string): string +} diff --git a/dist/Move Repeat Brackets for Chords.lua b/dist/Move Repeat Brackets for Chords.lua deleted file mode 100644 index 5bfa12f4..00000000 --- a/dist/Move Repeat Brackets for Chords.lua +++ /dev/null @@ -1,37 +0,0 @@ -function plugindef() - -- This function and the 'finaleplugin' namespace - -- are both reserved for the plug-in definition. - finaleplugin.RequireSelection = true - finaleplugin.Author = "Michael McClennan" - finaleplugin.Copyright = "©2021 Michael McClennan" - finaleplugin.Version = "1.0" - finaleplugin.Date = "May 22, 2021" - finaleplugin.AuthorEmail = "info@michaelmcclennan.com" - return "Move Repeat Brackets for Chords", "Move Repeat Brackets for Chords", "" -end - - -local region = finenv.Region() - for measure = region.StartMeasure, region.EndMeasure do - - local baseline = finale.FCBaseline() - baseline:LoadDefaultForMode(3) - local bracketpos = baseline.VerticalOffset - 145 - local userVar = 72 -- sets how far above the chord baseline the brackets should go (in EVPUs) - - local r = finale.FCEndingRepeat() - if r:Load(measure) then - r.VerticalTopBracketPosition = bracketpos + userVar -- adjusts bracket height - r.VerticalRightBracketPosition = bracketpos + userVar -- adjusts bracket height - r.VerticalTextPosition = bracketpos + userVar +25 --**height of repeat text - r:Save() - end - - for measure = region.StartMeasure, region.EndMeasure do - local b = finale.FCBackwardRepeat() - if b:Load(measure) then - b.TopBracketPosition = bracketpos + userVar --adjusts backwards repeat bracket height - b:Save() - end - end -end \ No newline at end of file diff --git a/dist/accidental_simplify.lua b/dist/accidental_simplify.lua index 4f95468b..80bae9c2 100644 --- a/dist/accidental_simplify.lua +++ b/dist/accidental_simplify.lua @@ -1,638 +1 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0" - finaleplugin.Date = "August 22, 2021" - finaleplugin.CategoryTags = "Accidental" - finaleplugin.AuthorURL = "https://nickmazuk.com" - return "Simplify accidentals", "Simplify accidentals", "Removes all double sharps and flats by respelling them" -end - ---[[ -$module Transposition - -A collection of helpful JW Lua transposition scripts. - -This library allows configuration of custom key signatures by means -of a configuration file called "custom_key_sig.config.txt" in the -"script_settings" subdirectory. However, RGP Lua (starting with version 0.58) -can read the correct custom key signature information directly from -Finale. Therefore, when you run this script with RGP Lua 0.58+, the configuration file -is ignored. -]] -- --- Structure --- 1. Helper functions --- 2. Diatonic Transposition --- 3. Enharmonic Transposition --- 3. Chromatic Transposition --- -local transposition = {} - --- Author: Robert Patterson --- Date: March 5, 2021 ---[[ -$module Configuration - -This library implements a UTF-8 text file scheme for configuration and user settings as follows: - -- Comments start with `--` -- Leading, trailing, and extra whitespace is ignored -- Each parameter is named and delimited as follows: - -``` - = -``` - -Parameter values may be: - -- Strings delimited with either single- or double-quotes -- Tables delimited with `{}` that may contain strings, booleans, or numbers -- Booleans (`true` or `false`) -- Numbers - -Currently the following are not supported: - -- Tables embedded within tables -- Tables containing strings that contain commas - -A sample configuration file might be: - -```lua --- Configuration File for "Hairpin and Dynamic Adjustments" script --- -left_dynamic_cushion = 12 --evpus -right_dynamic_cushion = -6 --evpus -``` - -## Configuration Files - -Configuration files provide a way for power users to modify script behavior without -having to modify the script itself. Some users track their changes to their configuration files, -so scripts should not create or modify them programmatically. - -- The user creates each configuration file in a subfolder called `script_settings` within -the folder of the calling script. -- Each script that has a configuration file defines its own configuration file name. -- It is entirely appropriate over time for scripts to transition from configuration files to user settings, -but this requires implementing a user interface to modify the user settings from within the script. -(See below.) - -## User Settings Files - -User settings are written by the scripts themselves and reside in the user's preferences folder -in an appropriately-named location for the operating system. (The naming convention is a detail that the -configuration library handles for the caller.) If the user settings are to be changed from their defaults, -the script itself should provide a means to change them. This could be a (preferably optional) dialog box -or any other mechanism the script author chooses. - -User settings are saved in the user's preferences folder (on Mac) or AppData folder (on Windows). - -## Merge Process - -Files are _merged_ into the passed-in list of default values. They do not _replace_ the list. Each calling script contains -a table of all the configurable parameters or settings it recognizes along with default values. An example: - -`sample.lua:` - -```lua -parameters = { - x = 1, - y = 2, - z = 3 -} - -configuration.get_parameters(parameters, "script.config.txt") - -for k, v in pairs(parameters) do - print(k, v) -end -``` - -Suppose the `script.config.text` file is as follows: - -``` -y = 4 -q = 6 -``` - -The returned parameters list is: - - -```lua -parameters = { - x = 1, -- remains the default value passed in - y = 4, -- replaced value from the config file - z = 3 -- remains the default value passed in -} -``` - -The `q` parameter in the config file is ignored because the input paramater list -had no `q` parameter. - -This approach allows total flexibility for the script add to or modify its list of parameters -without having to worry about older configuration files or user settings affecting it. -]] - -local configuration = {} - -local script_settings_dir = "script_settings" -- the parent of this directory is the running lua path -local comment_marker = "--" -local parameter_delimiter = "=" -local path_delimiter = "/" - -local file_exists = function(file_path) - local f = io.open(file_path, "r") - if nil ~= f then - io.close(f) - return true - end - return false -end - -local strip_leading_trailing_whitespace = function(str) - return str:match("^%s*(.-)%s*$") -- lua pattern magic taken from the Internet -end - -local parse_table = function(val_string) - local ret_table = {} - for element in val_string:gmatch("[^,%s]+") do -- lua pattern magic taken from the Internet - local parsed_element = parse_parameter(element) - table.insert(ret_table, parsed_element) - end - return ret_table -end - -parse_parameter = function(val_string) - if "\"" == val_string:sub(1, 1) and "\"" == val_string:sub(#val_string, #val_string) then -- double-quote string - return string.gsub(val_string, "\"(.+)\"", "%1") -- lua pattern magic: "(.+)" matches all characters between two double-quote marks (no escape chars) - elseif "'" == val_string:sub(1, 1) and "'" == val_string:sub(#val_string, #val_string) then -- single-quote string - return string.gsub(val_string, "'(.+)'", "%1") -- lua pattern magic: '(.+)' matches all characters between two single-quote marks (no escape chars) - elseif "{" == val_string:sub(1, 1) and "}" == val_string:sub(#val_string, #val_string) then - return parse_table(string.gsub(val_string, "{(.+)}", "%1")) - elseif "true" == val_string then - return true - elseif "false" == val_string then - return false - end - return tonumber(val_string) -end - -local get_parameters_from_file = function(file_path, parameter_list) - local file_parameters = {} - - if not file_exists(file_path) then - return false - end - - for line in io.lines(file_path) do - local comment_at = string.find(line, comment_marker, 1, true) -- true means find raw string rather than lua pattern - if nil ~= comment_at then - line = string.sub(line, 1, comment_at - 1) - end - local delimiter_at = string.find(line, parameter_delimiter, 1, true) - if nil ~= delimiter_at then - local name = strip_leading_trailing_whitespace(string.sub(line, 1, delimiter_at - 1)) - local val_string = strip_leading_trailing_whitespace(string.sub(line, delimiter_at + 1)) - file_parameters[name] = parse_parameter(val_string) - end - end - - for param_name, _ in pairs(parameter_list) do - local param_val = file_parameters[param_name] - if nil ~= param_val then - parameter_list[param_name] = param_val - end - end - - return true -end - ---[[ -% get_parameters - -Searches for a file with the input filename in the `script_settings` directory and replaces the default values in `parameter_list` -with any that are found in the config file. - -@ file_name (string) the file name of the config file (which will be prepended with the `script_settings` directory) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true if the file exists -]] -function configuration.get_parameters(file_name, parameter_list) - local path = "" - if finenv.IsRGPLua then - path = finenv.RunningLuaFolderPath() - else - local str = finale.FCString() - str:SetRunningLuaFolderPath() - path = str.LuaString - end - local file_path = path .. script_settings_dir .. path_delimiter .. file_name - return get_parameters_from_file(file_path, parameter_list) -end - --- Calculates a filepath in the user's preferences folder using recommended naming conventions --- -local calc_preferences_filepath = function(script_name) - local str = finale.FCString() - str:SetUserOptionsPath() - local folder_name = str.LuaString - if not finenv.IsRGPLua and finenv.UI():IsOnMac() then - -- works around bug in SetUserOptionsPath() in JW Lua - folder_name = os.getenv("HOME") .. folder_name:sub(2) -- strip '~' and replace with actual folder - end - if finenv.UI():IsOnWindows() then - folder_name = folder_name .. path_delimiter .. "FinaleLua" - end - local file_path = folder_name .. path_delimiter - if finenv.UI():IsOnMac() then - file_path = file_path .. "com.finalelua." - end - file_path = file_path .. script_name .. ".settings.txt" - return file_path, folder_name -end - ---[[ -% save_user_settings - -Saves the user's preferences for a script from the values provided in `parameter_list`. - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true on success -]] -function configuration.save_user_settings(script_name, parameter_list) - local file_path, folder_path = calc_preferences_filepath(script_name) - local file = io.open(file_path, "w") - if not file and finenv.UI():IsOnWindows() then -- file not found - os.execute('mkdir "' .. folder_path ..'"') -- so try to make a folder (windows only, since the folder is guaranteed to exist on mac) - file = io.open(file_path, "w") -- try the file again - end - if not file then -- still couldn't find file - return false -- so give up - end - file:write("-- User settings for " .. script_name .. ".lua\n\n") - for k,v in pairs(parameter_list) do -- only number, boolean, or string values - if type(v) == "string" then - v = "\"" .. v .."\"" - else - v = tostring(v) - end - file:write(k, " = ", v, "\n") - end - file:close() - return true -- success -end - ---[[ -% get_user_settings - -Find the user's settings for a script in the preferences directory and replaces the default values in `parameter_list` -with any that are found in the preferences file. The actual name and path of the preferences file is OS dependent, so -the input string should just be the script name (without an extension). - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -@ [create_automatically] (boolean) if true, create the file automatically (default is `true`) -: (boolean) `true` if the file already existed, `false` if it did not or if it was created automatically -]] -function configuration.get_user_settings(script_name, parameter_list, create_automatically) - if create_automatically == nil then create_automatically = true end - local exists = get_parameters_from_file(calc_preferences_filepath(script_name), parameter_list) - if not exists and create_automatically then - configuration.save_user_settings(script_name, parameter_list) - end - return exists -end - - - - -local standard_key_number_of_steps = 12 -local standard_key_major_diatonic_steps = {0, 2, 4, 5, 7, 9, 11} -local standard_key_minor_diatonic_steps = {0, 2, 3, 5, 7, 8, 10} - -local max_allowed_abs_alteration = 7 -- Finale cannot represent an alteration outside +/- 7 - --- first number is plus_fifths --- second number is minus_octaves -local diatonic_interval_adjustments = {{0, 0}, {2, -1}, {4, -2}, {-1, 1}, {1, 0}, {3, -1}, {5, -2}, {0, 1}} - -local custom_key_sig_config = { - number_of_steps = standard_key_number_of_steps, - diatonic_steps = standard_key_major_diatonic_steps, -} - -configuration.get_parameters("custom_key_sig.config.txt", custom_key_sig_config) - --- --- HELPER functions --- - -local sign = function(n) - if n < 0 then - return -1 - end - return 1 -end - --- this is necessary because the % operator in lua appears always to return a positive value, --- unlike the % operator in c++ -local signed_modulus = function(n, d) - return sign(n) * (math.abs(n) % d) -end - -local get_key = function(note) - local cell = finale.FCCell(note.Entry.Measure, note.Entry.Staff) - return cell:GetKeySignature() -end - --- These local functions that take FCKeySignature (key) as their first argument should --- perhaps move to a key_signature library someday. - --- return number of steps, diatonic steps map, and number of steps in fifth -local get_key_info = function(key) - local number_of_steps = standard_key_number_of_steps - local diatonic_steps = standard_key_major_diatonic_steps - if finenv.IsRGPLua and key.CalcTotalChromaticSteps then -- if this version of RGP Lua supports custom key sigs - number_of_steps = key:CalcTotalChromaticSteps() - diatonic_steps = key:CalcDiatonicStepsMap() - else - if not key:IsPredefined() then - number_of_steps = custom_key_sig_config.number_of_steps - diatonic_steps = custom_key_sig_config.diatonic_steps - elseif key:IsMinor() then - diatonic_steps = standard_key_minor_diatonic_steps - end - end - -- 0.5849625 is log(3/2)/log(2), which is how to calculate the 5th per Ere Lievonen. - -- For basically any practical key sig this calculation comes out to the 5th scale degree, - -- which is 7 chromatic steps for standard keys - local fifth_steps = math.floor((number_of_steps * 0.5849625) + 0.5) - return number_of_steps, diatonic_steps, fifth_steps -end - -local calc_scale_degree = function(interval, number_of_diatonic_steps_in_key) - local interval_normalized = signed_modulus(interval, number_of_diatonic_steps_in_key) - if interval_normalized < 0 then - interval_normalized = interval_normalized + number_of_diatonic_steps_in_key - end - return interval_normalized -end - -local calc_steps_between_scale_degrees = function(key, first_disp, second_disp) - local number_of_steps_in_key, diatonic_steps = get_key_info(key) - local first_scale_degree = calc_scale_degree(first_disp, #diatonic_steps) - local second_scale_degree = calc_scale_degree(second_disp, #diatonic_steps) - local number_of_steps = sign(second_disp - first_disp) * - (diatonic_steps[second_scale_degree + 1] - diatonic_steps[first_scale_degree + 1]) - if number_of_steps < 0 then - number_of_steps = number_of_steps + number_of_steps_in_key - end - return number_of_steps -end - -local calc_steps_in_alteration = function(key, interval, alteration) - local number_of_steps_in_key, _, fifth_steps = get_key_info(key) - local plus_fifths = sign(interval) * alteration * 7 -- number of fifths to add for alteration - local minus_octaves = sign(interval) * alteration * -4 -- number of octaves to subtract for alteration - local new_alteration = sign(interval) * ((plus_fifths * fifth_steps) + (minus_octaves * number_of_steps_in_key)) -- new alteration for chromatic interval - return new_alteration -end - -local calc_steps_in_normalized_interval = function(key, interval_normalized) - local number_of_steps_in_key, _, fifth_steps = get_key_info(key) - local plus_fifths = diatonic_interval_adjustments[math.abs(interval_normalized) + 1][1] -- number of fifths to add for interval - local minus_octaves = diatonic_interval_adjustments[math.abs(interval_normalized) + 1][2] -- number of octaves to subtract for alteration - local number_of_steps_in_interval = sign(interval_normalized) * - ((plus_fifths * fifth_steps) + (minus_octaves * number_of_steps_in_key)) - return number_of_steps_in_interval -end - -local simplify_spelling = function(note, min_abs_alteration) - while math.abs(note.RaiseLower) > min_abs_alteration do - local curr_sign = sign(note.RaiseLower) - local curr_abs_disp = math.abs(note.RaiseLower) - local direction = curr_sign - local success = transposition.enharmonic_transpose(note, direction, true) -- true: ignore errors (success is always true) - if not success then - return false - end - if math.abs(note.RaiseLower) >= curr_abs_disp then - return transposition.enharmonic_transpose(note, -1 * direction) - end - if curr_sign ~= sign(note.RaiseLower) then - break - end - end - return true -end - --- --- DIATONIC transposition (affect only Displacement) --- - ---[[ -% diatonic_transpose - -Transpose the note diatonically by the given interval displacement. - -@ note (FCNote) input and modified output -@ interval (number) 0 = unison, 1 = up a diatonic second, -2 = down a diatonic third, etc. -]] -function transposition.diatonic_transpose(note, interval) - note.Displacement = note.Displacement + interval -end - ---[[ -% change_octave - -Transpose the note by the given number of octaves. - -@ note (FCNote) input and modified output -@ number_of_octaves (number) 0 = no change, 1 = up an octave, -2 = down 2 octaves, etc. -]] -function transposition.change_octave(note, number_of_octaves) - transposition.diatonic_transpose(note, 7 * number_of_octaves) -end - --- --- ENHARMONIC transposition --- - ---[[ -% enharmonic_transpose - -Transpose the note enharmonically in the given direction. In some microtone systems this yields a different result than transposing by a diminished 2nd. -Failure occurs if the note's `RaiseLower` value exceeds an absolute value of 7. This is a hard-coded limit in Finale. - -@ note (FCNote) input and modified output -@ direction (number) positive = up, negative = down (normally 1 or -1, but any positive or negative numbers work) -@ [ignore_error] (boolean) default false. If true, always return success. External callers should omit this parameter. -: (boolean) success or failure -]] -function transposition.enharmonic_transpose(note, direction, ignore_error) - ignore_error = ignore_error or false - local curr_disp = note.Displacement - local curr_alt = note.RaiseLower - local key = get_key(note) - local key_step_enharmonic = calc_steps_between_scale_degrees( - key, note.Displacement, note.Displacement + sign(direction)) - transposition.diatonic_transpose(note, sign(direction)) - note.RaiseLower = note.RaiseLower - sign(direction) * key_step_enharmonic - if ignore_error then - return true - end - if math.abs(note.RaiseLower) > max_allowed_abs_alteration then - note.Displacement = curr_disp - note.RaiseLower = curr_alt - return false - end - return true -end - --- --- CHROMATIC transposition (affect Displacement and RaiseLower) --- - ---[[ -% chromatic_transpose - -Transposes a note chromatically by the input chromatic interval. Supports custom key signatures -and microtone systems by means of a `custom_key_sig.config.txt` file. In Finale, chromatic intervals -are defined by a diatonic displacement (0 = unison, 1 = second, 2 = third, etc.) and a chromatic alteration. -Major and perfect intervals have a chromatic alteration of 0. So for example, `{2, -1}` is up a minor third, `{3, 0}` -is up a perfect fourth, `{5, 1}` is up an augmented sixth, etc. Reversing the signs of both values in the pair -allows for downwards transposition. - -@ note (FCNote) the note to transpose -@ interval (number) the diatonic displacement (negative for transposing down) -@ alteration (number) the chromatic alteration that defines the chromatic interval (reverse sign for transposing down) -@ [simplify] (boolean) if present and true causes the spelling of the transposed note to be simplified -: (boolean) success or failure (see `enharmonic_transpose` for what causes failure) ---]] -function transposition.chromatic_transpose(note, interval, alteration, simplify) - simplify = simplify or false - local curr_disp = note.Displacement - local curr_alt = note.RaiseLower - - local key = get_key(note) - local number_of_steps, diatonic_steps, fifth_steps = get_key_info(key) - local interval_normalized = signed_modulus(interval, #diatonic_steps) - local steps_in_alteration = calc_steps_in_alteration(key, interval, alteration) - local steps_in_interval = calc_steps_in_normalized_interval(key, interval_normalized) - local steps_in_diatonic_interval = calc_steps_between_scale_degrees( - key, note.Displacement, note.Displacement + interval_normalized) - local effective_alteration = steps_in_alteration + steps_in_interval - sign(interval) * steps_in_diatonic_interval - transposition.diatonic_transpose(note, interval) - note.RaiseLower = note.RaiseLower + effective_alteration - - local min_abs_alteration = max_allowed_abs_alteration - if simplify then - min_abs_alteration = 0 - end - local success = simplify_spelling(note, min_abs_alteration) - if not success then -- if Finale can't represent the transposition, revert it to original value - note.Displacement = curr_disp - note.RaiseLower = curr_alt - end - return success -end - ---[[ -% stepwise_transpose - -Transposes the note by the input number of steps and simplifies the spelling. -For predefined key signatures, each step is a half-step. -For microtone systems defined with custom key signatures and matching options in the `custom_key_sig.config.txt` file, -each step is the smallest division of the octave defined by the custom key signature. - -@ note (FCNote) input and modified output -@ number_of_steps (number) positive = up, negative = down -: (boolean) success or failure (see `enharmonic_transpose` for what causes failure) -]] -function transposition.stepwise_transpose(note, number_of_steps) - local curr_disp = note.Displacement - local curr_alt = note.RaiseLower - note.RaiseLower = note.RaiseLower + number_of_steps - local success = simplify_spelling(note, 0) - if not success then -- if Finale can't represent the transposition, revert it to original value - note.Displacement = curr_disp - note.RaiseLower = curr_alt - end - return success -end - ---[[ -% chromatic_major_third_down - -Transpose the note down by a major third. - -@ note (FCNote) input and modified output -]] -function transposition.chromatic_major_third_down(note) - transposition.chromatic_transpose(note, -2, -0) -end - ---[[ -% chromatic_perfect_fourth_up - -Transpose the note up by a perfect fourth. - -@ note (FCNote) input and modified output -]] -function transposition.chromatic_perfect_fourth_up(note) - transposition.chromatic_transpose(note, 3, 0) -end - ---[[ -% chromatic_perfect_fifth_down - -Transpose the note down by a perfect fifth. - -@ note (FCNote) input and modified output -]] -function transposition.chromatic_perfect_fifth_down(note) - transposition.chromatic_transpose(note, -4, -0) -end - - - - -function accidentals_simplify() - for entry in eachentrysaved(finenv.Region()) do - if not entry:IsNote() then - goto continue_entry_loop - end - local measure_number = entry.Measure - local staff_number = entry.Staff - local cell = finale.FCCell(measure_number, staff_number) - local key_signature = cell:GetKeySignature() - - for note in each(entry) do - if note.RaiseLower == 0 then - goto continue_note_loop - end - - -- Use note_string rather than note.RaiseLower because - -- with key signatures, note.RaiseLower returns the alteration - -- from the key signature, not the actual accidental. - -- For instance, in the key of A, a Gb has a RaiseLower of -2 - -- even though Gb is not a double flat. - - local fs_note_string = finale.FCString() - note:GetString(fs_note_string, key_signature, false, false) - local note_string = fs_note_string:GetLuaString() - - -- checking for 'bb' and '##' will also match triple sharps and flats - if string.match(note_string, "bb") or string.match(note_string, "##") then - transposition.enharmonic_transpose(note, note.RaiseLower) - transposition.chromatic_transpose(note, 0, 0, true) - end - ::continue_note_loop:: - end - ::continue_entry_loop:: - end -end - -accidentals_simplify() +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Nick Mazuk"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0"finaleplugin.Date="August 22, 2021"finaleplugin.CategoryTags="Accidental"finaleplugin.AuthorURL="https://nickmazuk.com"return"Simplify accidentals","Simplify accidentals","Removes all double sharps and flats by respelling them"end;local o=require("library.transposition")function accidentals_simplify()for p in eachentrysaved(finenv.Region())do if not p:IsNote()then goto q end;local r=p.Measure;local s=p.Staff;local t=finale.FCCell(r,s)local u=t:GetKeySignature()for v in each(p)do if v.RaiseLower==0 then goto w end;local x=finale.FCString()v:GetString(x,u,false,false)local y=x:GetLuaString()if string.match(y,"bb")or string.match(y,"##")then o.enharmonic_transpose(v,v.RaiseLower)o.chromatic_transpose(v,0,0,true)end::w::end::q::end end;accidentals_simplify()end)c("library.transposition",function(require,n,c,d)local z={}function z.finale_version(A,B,C)local D=bit32.bor(bit32.lshift(math.floor(A),24),bit32.lshift(math.floor(B),20))if C then D=bit32.bor(D,math.floor(C))end;return D end;function z.group_overlaps_region(E,F)if F:IsFullDocumentSpan()then return true end;local G=false;local H=finale.FCSystemStaves()H:LoadAllForRegion(F)for I in each(H)do if E:ContainsStaff(I:GetStaff())then G=true;break end end;if not G then return false end;if E.StartMeasure>F.EndMeasure or E.EndMeasure0 then P=true;break end;N=N+1 end;if P then local Q=finale.FCStaffSystem()Q:Load(O:GetFirstSystem())return finale.FCCell(Q.FirstMeasure,Q.TopStaff)end;local R=finale.FCMusicRegion()R:SetFullDocument()return finale.FCCell(R.EndMeasure,R.EndStaff)end;function z.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local S=finale.FCMusicRegion()S:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),S.StartStaff)end;return z.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function z.get_top_left_selected_or_visible_cell()local L=finenv.Region()if not L:IsEmpty()then return finale.FCCell(L.StartMeasure,L.StartStaff)end;return z.get_top_left_visible_cell()end;function z.is_default_measure_number_visible_on_cell(T,t,U,V)local W=finale.FCCurrentStaffSpec()if not W:LoadForCell(t,0)then return false end;if T:GetShowOnTopStaff()and t.Staff==U.TopStaff then return true end;if T:GetShowOnBottomStaff()and t.Staff==U:CalcBottomStaff()then return true end;if W.ShowMeasureNumbers then return not T:GetExcludeOtherStaves(V)end;return false end;function z.is_default_number_visible_and_left_aligned(T,t,X,V,Y)if T.UseScoreInfoForParts then V=false end;if Y and T:GetShowOnMultiMeasureRests(V)then if finale.MNALIGN_LEFT~=T:GetMultiMeasureAlignment(V)then return false end elseif t.Measure==X.FirstMeasure then if not T:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=T:GetStartAlignment(V)then return false end else if not T:GetShowMultiples(V)then return false end;if finale.MNALIGN_LEFT~=T:GetMultipleAlignment(V)then return false end end;return z.is_default_measure_number_visible_on_cell(T,t,X,V)end;function z.update_layout(Z,_)Z=Z or 1;_=_ or false;local a0=finale.FCPage()if a0:Load(Z)then a0:UpdateLayout(_)end end;function z.get_current_part()local a1=finale.FCParts()a1:LoadAll()return a1:GetCurrent()end;function z.get_page_format_prefs()local a2=z.get_current_part()local a3=finale.FCPageFormatPrefs()local a4=false;if a2:IsScore()then a4=a3:LoadScore()else a4=a3:LoadParts()end;return a3,a4 end;function z.get_smufl_metadata_file(a5)if not a5 then a5=finale.FCFontInfo()a5:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local a6=function(a7,a5)local a8=a7 .."/SMuFL/Fonts/"..a5.Name.."/"..a5.Name..".json"return io.open(a8,"r")end;local a9=""if finenv.UI():IsOnWindows()then a9=os.getenv("LOCALAPPDATA")else a9=os.getenv("HOME").."/Library/Application Support"end;local aa=a6(a9,a5)if nil~=aa then return aa end;local ab="/Library/Application Support"if finenv.UI():IsOnWindows()then ab=os.getenv("COMMONPROGRAMFILES")end;return a6(ab,a5)end;function z.is_font_smufl_font(a5)if not a5 then a5=finale.FCFontInfo()a5:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=z.finale_version(27,1)then if nil~=a5.IsSMuFLFont then return a5.IsSMuFLFont end end;local ac=z.get_smufl_metadata_file(a5)if nil~=ac then io.close(ac)return true end;return false end;function z.simple_input(ad,ae)local af=finale.FCString()af.LuaString=""local ag=finale.FCString()local ah=160;function format_ctrl(ai,aj,ak,al)ai:SetHeight(aj)ai:SetWidth(ak)ag.LuaString=al;ai:SetText(ag)end;title_width=string.len(ad)*6+54;if title_width>ah then ah=title_width end;text_width=string.len(ae)*6;if text_width>ah then ah=text_width end;ag.LuaString=ad;local am=finale.FCCustomLuaWindow()am:SetTitle(ag)local an=am:CreateStatic(0,0)format_ctrl(an,16,ah,ae)local ao=am:CreateEdit(0,20)format_ctrl(ao,20,ah,"")am:CreateOkButton()am:CreateCancelButton()function callback(ai)end;am:RegisterHandleCommand(callback)if am:ExecuteModal(nil)==finale.EXECMODAL_OK then af.LuaString=ao:GetText(af)return af.LuaString end end;function z.is_finale_object(ap)return ap and type(ap)=="userdata"and ap.ClassName and ap.GetClassID and true or false end;function z.system_indent_set_to_prefs(X,a3)a3=a3 or z.get_page_format_prefs()local aq=finale.FCMeasure()local ar=X.FirstMeasure==1;if not ar and aq:Load(X.FirstMeasure)then if aq.ShowFullNames then ar=true end end;if ar and a3.UseFirstSystemMargins then X.LeftMargin=a3.FirstSystemLeft else X.LeftMargin=a3.SystemLeft end;return X:Save()end;return z end)return a("__root") \ No newline at end of file diff --git a/dist/articulation_autoposition_rolled_chords.lua b/dist/articulation_autoposition_rolled_chords.lua index 0cd9e957..77a634db 100644 --- a/dist/articulation_autoposition_rolled_chords.lua +++ b/dist/articulation_autoposition_rolled_chords.lua @@ -1,12 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0" - finaleplugin.Date = "December 26, 2021" - finaleplugin.CategoryTags = "Articulation" - finaleplugin.MinJWLuaVersion = 0.59 - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Robert Patterson"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0"finaleplugin.Date="December 26, 2021"finaleplugin.CategoryTags="Articulation"finaleplugin.MinJWLuaVersion=0.59;finaleplugin.Notes=[[ How to use this script: 1. Manually apply rolled-chord articulations to the chords that need them (without worrying about how they look). @@ -25,993 +17,4 @@ function plugindef() the script. This script requires RGP Lua 0.59 or later. - ]] - return "Autoposition Rolled Chord Articulations", "Autoposition Rolled Chord Articulations", - 'Automatically positions rolled chords and other articulations with "Copy Main Symbol Vertically" set.' -end - --- This script requires the VerticalCopyToPos property on FCArticulation, which was added in v0.59 of RGP Lua. --- Therefore, it is marked not to load in any earlier version. - ---[[ -$module Note Entry -]] -- -local note_entry = {} - ---[[ -% get_music_region - -Returns an intance of `FCMusicRegion` that corresponds to the metric location of the input note entry. - -@ entry (FCNoteEntry) -: (FCMusicRegion) -]] -function note_entry.get_music_region(entry) - local exp_region = finale.FCMusicRegion() - exp_region:SetCurrentSelection() -- called to match the selected IU list (e.g., if using Staff Sets) - exp_region.StartStaff = entry.Staff - exp_region.EndStaff = entry.Staff - exp_region.StartMeasure = entry.Measure - exp_region.EndMeasure = entry.Measure - exp_region.StartMeasurePos = entry.MeasurePos - exp_region.EndMeasurePos = entry.MeasurePos - return exp_region -end - --- entry_metrics can be omitted, in which case they are constructed and released here --- return entry_metrics, loaded_here -local use_or_get_passed_in_entry_metrics = function(entry, entry_metrics) - if entry_metrics then - return entry_metrics, false - end - entry_metrics = finale.FCEntryMetrics() - if entry_metrics:Load(entry) then - return entry_metrics, true - end - return nil, false -end - ---[[ -% get_evpu_notehead_height - -Returns the calculated height of the notehead rectangle. - -@ entry (FCNoteEntry) - -: (number) the EVPU height -]] -function note_entry.get_evpu_notehead_height(entry) - local highest_note = entry:CalcHighestNote(nil) - local lowest_note = entry:CalcLowestNote(nil) - local evpu_height = (2 + highest_note:CalcStaffPosition() - lowest_note:CalcStaffPosition()) * 12 -- 12 evpu per staff step; add 2 staff steps to accommodate for notehead height at top and bottom - return evpu_height -end - ---[[ -% get_top_note_position - -Returns the vertical page coordinate of the top of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_top_note_position(entry, entry_metrics) - local retval = -math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if not entry:CalcStemUp() then - retval = entry_metrics.TopPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.BottomPosition + scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% get_bottom_note_position - -Returns the vertical page coordinate of the bottom of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_bottom_note_position(entry, entry_metrics) - local retval = math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if entry:CalcStemUp() then - retval = entry_metrics.BottomPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.TopPosition - scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% calc_widths - -Get the widest left-side notehead width and widest right-side notehead width. - -@ entry (FCNoteEntry) -: (number, number) widest left-side notehead width and widest right-side notehead width -]] -function note_entry.calc_widths(entry) - local left_width = 0 - local right_width = 0 - for note in each(entry) do - local note_width = note:CalcNoteheadWidth() - if note_width > 0 then - if note:CalcRightsidePlacement() then - if note_width > right_width then - right_width = note_width - end - else - if note_width > left_width then - left_width = note_width - end - end - end - end - return left_width, right_width -end - --- These functions return the offset for an expression handle. --- Expression handles are vertical when they are left-aligned --- with the primary notehead rectangle. - ---[[ -% calc_left_of_all_noteheads - -Calculates the handle offset for an expression with "Left of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_all_noteheads(entry) - if entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return -left -end - ---[[ -% calc_left_of_primary_notehead - -Calculates the handle offset for an expression with "Left of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_primary_notehead(entry) - return 0 -end - ---[[ -% calc_center_of_all_noteheads - -Calculates the handle offset for an expression with "Center of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - local width_centered = (left + right) / 2 - if not entry:CalcStemUp() then - width_centered = width_centered - left - end - return width_centered -end - ---[[ -% calc_center_of_primary_notehead - -Calculates the handle offset for an expression with "Center of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_primary_notehead(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left / 2 - end - return right / 2 -end - ---[[ -% calc_stem_offset - -Calculates the offset of the stem from the left edge of the notehead rectangle. Eventually the PDK Framework may be able to provide this instead. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset of stem from the left edge of the notehead rectangle. -]] -function note_entry.calc_stem_offset(entry) - if not entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return left -end - ---[[ -% calc_right_of_all_noteheads - -Calculates the handle offset for an expression with "Right of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_right_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left + right - end - return right -end - ---[[ -% calc_note_at_index - -This function assumes `for note in each(note_entry)` always iterates in the same direction. -(Knowing how the Finale PDK works, it probably iterates from bottom to top note.) -Currently the PDK Framework does not seem to offer a better option. - -@ entry (FCNoteEntry) -@ note_index (number) the zero-based index -]] -function note_entry.calc_note_at_index(entry, note_index) - local x = 0 - for note in each(entry) do - if x == note_index then - return note - end - x = x + 1 - end - return nil -end - ---[[ -% stem_sign - -This is useful for many x,y positioning fields in Finale that mirror +/- -based on stem direction. - -@ entry (FCNoteEntry) -: (number) 1 if upstem, -1 otherwise -]] -function note_entry.stem_sign(entry) - if entry:CalcStemUp() then - return 1 - end - return -1 -end - ---[[ -% duplicate_note - -@ note (FCNote) -: (FCNote | nil) reference to added FCNote or `nil` if not success -]] -function note_entry.duplicate_note(note) - local new_note = note.Entry:AddNewNote() - if nil ~= new_note then - new_note.Displacement = note.Displacement - new_note.RaiseLower = note.RaiseLower - new_note.Tie = note.Tie - new_note.TieBackwards = note.TieBackwards - end - return new_note -end - ---[[ -% delete_note - -Removes the specified FCNote from its associated FCNoteEntry. - -@ note (FCNote) -: (boolean) true if success -]] -function note_entry.delete_note(note) - local entry = note.Entry - if nil == entry then - return false - end - - -- attempt to delete all associated entry-detail mods, but ignore any failures - finale.FCAccidentalMod():EraseAt(note) - finale.FCCrossStaffMod():EraseAt(note) - finale.FCDotMod():EraseAt(note) - finale.FCNoteheadMod():EraseAt(note) - finale.FCPercussionNoteMod():EraseAt(note) - finale.FCTablatureNoteMod():EraseAt(note) - if finale.FCTieMod then -- added in RGP Lua 0.62 - finale.FCTieMod(finale.TIEMODTYPE_TIESTART):EraseAt(note) - finale.FCTieMod(finale.TIEMODTYPE_TIEEND):EraseAt(note) - end - - return entry:DeleteNote(note) -end - ---[[ -% calc_spans_number_of_octaves - -Calculates the numer of octaves spanned by a chord (considering only staff positions, not accidentals). - -@ entry (FCNoteEntry) the entry to calculate from -: (number) of octaves spanned -]] -function note_entry.calc_spans_number_of_octaves(entry) - local top_note = entry:CalcHighestNote(nil) - local bottom_note = entry:CalcLowestNote(nil) - local displacement_diff = top_note.Displacement - bottom_note.Displacement - local num_octaves = math.ceil(displacement_diff / 7) - return num_octaves -end - ---[[ -% add_augmentation_dot - -Adds an augentation dot to the entry. This works even if the entry already has one or more augmentation dots. - -@ entry (FCNoteEntry) the entry to which to add the augmentation dot -]] -function note_entry.add_augmentation_dot(entry) - -- entry.Duration = entry.Duration | (entry.Duration >> 1) -- For Lua 5.3 and higher - entry.Duration = bit32.bor(entry.Duration, bit32.rshift(entry.Duration, 1)) -end - ---[[ -% get_next_same_v - -Returns the next entry in the same V1 or V2 as the input entry. -If the input entry is V2, only the current V2 launch is searched. -If the input entry is V1, only the current measure and layer is searched. - -@ entry (FCNoteEntry) the entry to process -: (FCNoteEntry) the next entry or `nil` in none -]] -function note_entry.get_next_same_v(entry) - local next_entry = entry:Next() - if entry.Voice2 then - if (nil ~= next_entry) and next_entry.Voice2 then - return next_entry - end - return nil - end - if entry.Voice2Launch then - while (nil ~= next_entry) and next_entry.Voice2 do - next_entry = next_entry:Next() - end - end - return next_entry -end - ---[[ -% hide_stem - -Hides the stem of the entry by replacing it with Shape 0. - -@ entry (FCNoteEntry) the entry to process -]] -function note_entry.hide_stem(entry) - local stem = finale.FCCustomStemMod() - stem:SetNoteEntry(entry) - stem:UseUpStemData(entry:CalcStemUp()) - if stem:LoadFirst() then - stem.ShapeID = 0 - stem:Save() - else - stem.ShapeID = 0 - stem:SaveNew() - end -end - - - ---[[ -$module Articulation -]] -- -local articulation = {} - ---[[ -$module Note Entry -]] -- -local note_entry = {} - ---[[ -% get_music_region - -Returns an intance of `FCMusicRegion` that corresponds to the metric location of the input note entry. - -@ entry (FCNoteEntry) -: (FCMusicRegion) -]] -function note_entry.get_music_region(entry) - local exp_region = finale.FCMusicRegion() - exp_region:SetCurrentSelection() -- called to match the selected IU list (e.g., if using Staff Sets) - exp_region.StartStaff = entry.Staff - exp_region.EndStaff = entry.Staff - exp_region.StartMeasure = entry.Measure - exp_region.EndMeasure = entry.Measure - exp_region.StartMeasurePos = entry.MeasurePos - exp_region.EndMeasurePos = entry.MeasurePos - return exp_region -end - --- entry_metrics can be omitted, in which case they are constructed and released here --- return entry_metrics, loaded_here -local use_or_get_passed_in_entry_metrics = function(entry, entry_metrics) - if entry_metrics then - return entry_metrics, false - end - entry_metrics = finale.FCEntryMetrics() - if entry_metrics:Load(entry) then - return entry_metrics, true - end - return nil, false -end - ---[[ -% get_evpu_notehead_height - -Returns the calculated height of the notehead rectangle. - -@ entry (FCNoteEntry) - -: (number) the EVPU height -]] -function note_entry.get_evpu_notehead_height(entry) - local highest_note = entry:CalcHighestNote(nil) - local lowest_note = entry:CalcLowestNote(nil) - local evpu_height = (2 + highest_note:CalcStaffPosition() - lowest_note:CalcStaffPosition()) * 12 -- 12 evpu per staff step; add 2 staff steps to accommodate for notehead height at top and bottom - return evpu_height -end - ---[[ -% get_top_note_position - -Returns the vertical page coordinate of the top of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_top_note_position(entry, entry_metrics) - local retval = -math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if not entry:CalcStemUp() then - retval = entry_metrics.TopPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.BottomPosition + scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% get_bottom_note_position - -Returns the vertical page coordinate of the bottom of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_bottom_note_position(entry, entry_metrics) - local retval = math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if entry:CalcStemUp() then - retval = entry_metrics.BottomPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.TopPosition - scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% calc_widths - -Get the widest left-side notehead width and widest right-side notehead width. - -@ entry (FCNoteEntry) -: (number, number) widest left-side notehead width and widest right-side notehead width -]] -function note_entry.calc_widths(entry) - local left_width = 0 - local right_width = 0 - for note in each(entry) do - local note_width = note:CalcNoteheadWidth() - if note_width > 0 then - if note:CalcRightsidePlacement() then - if note_width > right_width then - right_width = note_width - end - else - if note_width > left_width then - left_width = note_width - end - end - end - end - return left_width, right_width -end - --- These functions return the offset for an expression handle. --- Expression handles are vertical when they are left-aligned --- with the primary notehead rectangle. - ---[[ -% calc_left_of_all_noteheads - -Calculates the handle offset for an expression with "Left of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_all_noteheads(entry) - if entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return -left -end - ---[[ -% calc_left_of_primary_notehead - -Calculates the handle offset for an expression with "Left of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_primary_notehead(entry) - return 0 -end - ---[[ -% calc_center_of_all_noteheads - -Calculates the handle offset for an expression with "Center of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - local width_centered = (left + right) / 2 - if not entry:CalcStemUp() then - width_centered = width_centered - left - end - return width_centered -end - ---[[ -% calc_center_of_primary_notehead - -Calculates the handle offset for an expression with "Center of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_primary_notehead(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left / 2 - end - return right / 2 -end - ---[[ -% calc_stem_offset - -Calculates the offset of the stem from the left edge of the notehead rectangle. Eventually the PDK Framework may be able to provide this instead. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset of stem from the left edge of the notehead rectangle. -]] -function note_entry.calc_stem_offset(entry) - if not entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return left -end - ---[[ -% calc_right_of_all_noteheads - -Calculates the handle offset for an expression with "Right of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_right_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left + right - end - return right -end - ---[[ -% calc_note_at_index - -This function assumes `for note in each(note_entry)` always iterates in the same direction. -(Knowing how the Finale PDK works, it probably iterates from bottom to top note.) -Currently the PDK Framework does not seem to offer a better option. - -@ entry (FCNoteEntry) -@ note_index (number) the zero-based index -]] -function note_entry.calc_note_at_index(entry, note_index) - local x = 0 - for note in each(entry) do - if x == note_index then - return note - end - x = x + 1 - end - return nil -end - ---[[ -% stem_sign - -This is useful for many x,y positioning fields in Finale that mirror +/- -based on stem direction. - -@ entry (FCNoteEntry) -: (number) 1 if upstem, -1 otherwise -]] -function note_entry.stem_sign(entry) - if entry:CalcStemUp() then - return 1 - end - return -1 -end - ---[[ -% duplicate_note - -@ note (FCNote) -: (FCNote | nil) reference to added FCNote or `nil` if not success -]] -function note_entry.duplicate_note(note) - local new_note = note.Entry:AddNewNote() - if nil ~= new_note then - new_note.Displacement = note.Displacement - new_note.RaiseLower = note.RaiseLower - new_note.Tie = note.Tie - new_note.TieBackwards = note.TieBackwards - end - return new_note -end - ---[[ -% delete_note - -Removes the specified FCNote from its associated FCNoteEntry. - -@ note (FCNote) -: (boolean) true if success -]] -function note_entry.delete_note(note) - local entry = note.Entry - if nil == entry then - return false - end - - -- attempt to delete all associated entry-detail mods, but ignore any failures - finale.FCAccidentalMod():EraseAt(note) - finale.FCCrossStaffMod():EraseAt(note) - finale.FCDotMod():EraseAt(note) - finale.FCNoteheadMod():EraseAt(note) - finale.FCPercussionNoteMod():EraseAt(note) - finale.FCTablatureNoteMod():EraseAt(note) - if finale.FCTieMod then -- added in RGP Lua 0.62 - finale.FCTieMod(finale.TIEMODTYPE_TIESTART):EraseAt(note) - finale.FCTieMod(finale.TIEMODTYPE_TIEEND):EraseAt(note) - end - - return entry:DeleteNote(note) -end - ---[[ -% calc_spans_number_of_octaves - -Calculates the numer of octaves spanned by a chord (considering only staff positions, not accidentals). - -@ entry (FCNoteEntry) the entry to calculate from -: (number) of octaves spanned -]] -function note_entry.calc_spans_number_of_octaves(entry) - local top_note = entry:CalcHighestNote(nil) - local bottom_note = entry:CalcLowestNote(nil) - local displacement_diff = top_note.Displacement - bottom_note.Displacement - local num_octaves = math.ceil(displacement_diff / 7) - return num_octaves -end - ---[[ -% add_augmentation_dot - -Adds an augentation dot to the entry. This works even if the entry already has one or more augmentation dots. - -@ entry (FCNoteEntry) the entry to which to add the augmentation dot -]] -function note_entry.add_augmentation_dot(entry) - -- entry.Duration = entry.Duration | (entry.Duration >> 1) -- For Lua 5.3 and higher - entry.Duration = bit32.bor(entry.Duration, bit32.rshift(entry.Duration, 1)) -end - ---[[ -% get_next_same_v - -Returns the next entry in the same V1 or V2 as the input entry. -If the input entry is V2, only the current V2 launch is searched. -If the input entry is V1, only the current measure and layer is searched. - -@ entry (FCNoteEntry) the entry to process -: (FCNoteEntry) the next entry or `nil` in none -]] -function note_entry.get_next_same_v(entry) - local next_entry = entry:Next() - if entry.Voice2 then - if (nil ~= next_entry) and next_entry.Voice2 then - return next_entry - end - return nil - end - if entry.Voice2Launch then - while (nil ~= next_entry) and next_entry.Voice2 do - next_entry = next_entry:Next() - end - end - return next_entry -end - ---[[ -% hide_stem - -Hides the stem of the entry by replacing it with Shape 0. - -@ entry (FCNoteEntry) the entry to process -]] -function note_entry.hide_stem(entry) - local stem = finale.FCCustomStemMod() - stem:SetNoteEntry(entry) - stem:UseUpStemData(entry:CalcStemUp()) - if stem:LoadFirst() then - stem.ShapeID = 0 - stem:Save() - else - stem.ShapeID = 0 - stem:SaveNew() - end -end - - - - ---[[ -% delete_from_entry_by_char_num - -Removes any articulation assignment that has the specified character as its above-character. - -@ entry (FCNoteEntry) -@ char_num (number) UTF-32 code of character (which is the same as ASCII for ASCII characters) -]] -function articulation.delete_from_entry_by_char_num(entry, char_num) - local artics = entry:CreateArticulations() - for a in eachbackwards(artics) do - local defs = a:CreateArticulationDef() - if defs:GetAboveSymbolChar() == char_num then - a:DeleteData() - end - end -end - ---[[ -% is_note_side - -Uses `FCArticulation.CalcMetricPos` to determine if the input articulation is on the note-side. - -@ artic (FCArticulation) -@ [curr_pos] (FCPoint) current position of articulation that will be calculated if not supplied -: (boolean) true if on note-side, otherwise false -]] -function articulation.is_note_side(artic, curr_pos) - if nil == curr_pos then - curr_pos = finale.FCPoint(0, 0) - if not artic:CalcMetricPos(curr_pos) then - return false - end - end - local entry = artic:GetNoteEntry() - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil == cell_metrics then - return false - end - if entry:CalcStemUp() then - local bot_pos = note_entry.get_bottom_note_position(entry) - bot_pos = math.floor(((10000 * bot_pos) / cell_metrics.StaffScaling) + 0.5) - return curr_pos.Y <= bot_pos - else - local top_pos = note_entry.get_top_note_position(entry) - top_pos = math.floor(((10000 * top_pos) / cell_metrics.StaffScaling) + 0.5) - return curr_pos.Y >= top_pos - end - return false -end - ---[[ -% calc_main_character_dimensions - -Uses `FCTextMetrics:LoadArticulation` to determine the dimensions of the main character - -@ artic_def (FCArticulationDef) -: (number, number) the width and height of the main articulation character in (possibly fractional) evpus, or 0, 0 if it failed to load metrics -]] -function articulation.calc_main_character_dimensions(artic_def) - local text_mets = finale.FCTextMetrics() - if not text_mets:LoadArticulation(artic_def, false, 100) then - return 0, 0 - end - return text_mets:CalcWidthEVPUs(), text_mets:CalcHeightEVPUs() -end - - - - -local config = { - extend_across_staves = true, - -- per Ted Ross p. 198-9, the vertical extent is "approximately 1 space above and below" (counted from the staff position), - -- which means a half space from the note tips. But Gould has them closer, so we'll compromise here. - vertical_padding = 6, -- 1/4 space - -- per Ted Ross p. 198-9, the rolled chord mark precedes the chord by 3/4 space, and Gould (p. 131ff.) seems to agree - -- from looking at illustrations - horizontal_padding = 18 -- 3/4 space -} - -if finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_ALT) or finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_SHIFT) then - config.extend_across_staves = false -end - -function calc_top_bot_page_pos(search_region, artic_id) - local success = false - local top_page_pos = -math.huge - local bot_page_pos = math.huge - local left_page_pos = math.huge - for entry in eachentry(search_region) do - if not entry:IsRest() then - local artics = entry:CreateArticulations() - local got1 = false - for artic in each(artics) do - if artic.ID == artic_id then - got1 = true - break - end - end - if got1 then - local em = finale.FCEntryMetrics() - if em:Load(entry) then - success = true - local this_top = note_entry.get_top_note_position(entry,em) - if this_top > top_page_pos then - top_page_pos = this_top - end - local this_bottom = note_entry.get_bottom_note_position(entry,em) - if this_bottom < bot_page_pos then - bot_page_pos = this_bottom - end - if em.FirstAccidentalPosition < left_page_pos then - left_page_pos = em.FirstAccidentalPosition - end - em:FreeMetrics() - end - end - end - end - return success, top_page_pos, bot_page_pos, left_page_pos -end - -function articulation_autoposition_rolled_chords() - for entry in eachentry(finenv.Region()) do - local artics = entry:CreateArticulations() - for artic in each(artics) do - if artic.Visible then - local artic_def = artic:CreateArticulationDef() - if artic_def.CopyMainSymbol and not artic_def.CopyMainSymbolHorizontally then - local save_it = false - local search_region = note_entry.get_music_region(entry) - if config.extend_across_staves then - if search_region.StartStaff ~= finenv.Region().StartStaff then - artic.Visible = false - save_it = true - else - search_region.EndStaff = finenv.Region().EndStaff - end - end - local metric_pos = finale.FCPoint(0, 0) - local mm = finale.FCCellMetrics() - if artic.Visible and artic:CalcMetricPos(metric_pos) and mm:LoadAtEntry(entry) then - local success, top_page_pos, bottom_page_pos, left_page_pos = calc_top_bot_page_pos(search_region, artic.ID) - local this_bottom = note_entry.get_bottom_note_position(entry) - staff_scale = mm.StaffScaling / 10000 - top_page_pos = top_page_pos / staff_scale - bottom_page_pos = bottom_page_pos / staff_scale - left_page_pos = left_page_pos / staff_scale - this_bottom = this_bottom / staff_scale - local char_width, char_height = articulation.calc_main_character_dimensions(artic_def) - local half_char_height = char_height/2 - local horz_diff = left_page_pos - metric_pos.X - local vert_diff = top_page_pos - metric_pos.Y - artic.HorizontalPos = artic.HorizontalPos + math.floor(horz_diff - char_width - config.horizontal_padding + 0.5) - artic.VerticalPos = artic.VerticalPos + math.floor(vert_diff - char_height + 2*config.vertical_padding + 0.5) - artic.VerticalCopyToPos = math.floor(bottom_page_pos - this_bottom - config.vertical_padding - half_char_height + 0.5) - save_it = true - end - if save_it then - artic:Save() - end - end - end - end - end -end - -articulation_autoposition_rolled_chords() + ]]return"Autoposition Rolled Chord Articulations","Autoposition Rolled Chord Articulations",'Automatically positions rolled chords and other articulations with "Copy Main Symbol Vertically" set.'end;local o=require("library.note_entry")local p=require("library.articulation")local q={extend_across_staves=true,vertical_padding=6,horizontal_padding=18}if finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_ALT)or finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_SHIFT)then q.extend_across_staves=false end;function calc_top_bot_page_pos(r,s)local t=false;local u=-math.huge;local v=math.huge;local w=math.huge;for x in eachentry(r)do if not x:IsRest()then local y=x:CreateArticulations()local z=false;for A in each(y)do if A.ID==s then z=true;break end end;if z then local B=finale.FCEntryMetrics()if B:Load(x)then t=true;local C=o.get_top_note_position(x,B)if C>u then u=C end;local D=o.get_bottom_note_position(x,B)if DU.EndMeasure or T.EndMeasure0 then z=true;break end;a1=a1+1 end;if z then local a3=finale.FCStaffSystem()a3:Load(a2:GetFirstSystem())return finale.FCCell(a3.FirstMeasure,a3.TopStaff)end;local a4=finale.FCMusicRegion()a4:SetFullDocument()return finale.FCCell(a4.EndMeasure,a4.EndStaff)end;function O.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local a5=finale.FCMusicRegion()a5:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),a5.StartStaff)end;return O.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function O.get_top_left_selected_or_visible_cell()local _=finenv.Region()if not _:IsEmpty()then return finale.FCCell(_.StartMeasure,_.StartStaff)end;return O.get_top_left_visible_cell()end;function O.is_default_measure_number_visible_on_cell(a6,a7,a8,a9)local aa=finale.FCCurrentStaffSpec()if not aa:LoadForCell(a7,0)then return false end;if a6:GetShowOnTopStaff()and a7.Staff==a8.TopStaff then return true end;if a6:GetShowOnBottomStaff()and a7.Staff==a8:CalcBottomStaff()then return true end;if aa.ShowMeasureNumbers then return not a6:GetExcludeOtherStaves(a9)end;return false end;function O.is_default_number_visible_and_left_aligned(a6,a7,ab,a9,ac)if a6.UseScoreInfoForParts then a9=false end;if ac and a6:GetShowOnMultiMeasureRests(a9)then if finale.MNALIGN_LEFT~=a6:GetMultiMeasureAlignment(a9)then return false end elseif a7.Measure==ab.FirstMeasure then if not a6:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=a6:GetStartAlignment(a9)then return false end else if not a6:GetShowMultiples(a9)then return false end;if finale.MNALIGN_LEFT~=a6:GetMultipleAlignment(a9)then return false end end;return O.is_default_measure_number_visible_on_cell(a6,a7,ab,a9)end;function O.update_layout(ad,ae)ad=ad or 1;ae=ae or false;local af=finale.FCPage()if af:Load(ad)then af:UpdateLayout(ae)end end;function O.get_current_part()local ag=finale.FCParts()ag:LoadAll()return ag:GetCurrent()end;function O.get_page_format_prefs()local ah=O.get_current_part()local ai=finale.FCPageFormatPrefs()local t=false;if ah:IsScore()then t=ai:LoadScore()else t=ai:LoadParts()end;return ai,t end;function O.get_smufl_metadata_file(aj)if not aj then aj=finale.FCFontInfo()aj:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local ak=function(al,aj)local am=al.."/SMuFL/Fonts/"..aj.Name.."/"..aj.Name..".json"return io.open(am,"r")end;local an=""if finenv.UI():IsOnWindows()then an=os.getenv("LOCALAPPDATA")else an=os.getenv("HOME").."/Library/Application Support"end;local ao=ak(an,aj)if nil~=ao then return ao end;local ap="/Library/Application Support"if finenv.UI():IsOnWindows()then ap=os.getenv("COMMONPROGRAMFILES")end;return ak(ap,aj)end;function O.is_font_smufl_font(aj)if not aj then aj=finale.FCFontInfo()aj:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=O.finale_version(27,1)then if nil~=aj.IsSMuFLFont then return aj.IsSMuFLFont end end;local aq=O.get_smufl_metadata_file(aj)if nil~=aq then io.close(aq)return true end;return false end;function O.simple_input(ar,as)local at=finale.FCString()at.LuaString=""local au=finale.FCString()local av=160;function format_ctrl(aw,ax,ay,az)aw:SetHeight(ax)aw:SetWidth(ay)au.LuaString=az;aw:SetText(au)end;title_width=string.len(ar)*6+54;if title_width>av then av=title_width end;text_width=string.len(as)*6;if text_width>av then av=text_width end;au.LuaString=ar;local aA=finale.FCCustomLuaWindow()aA:SetTitle(au)local aB=aA:CreateStatic(0,0)format_ctrl(aB,16,av,as)local aC=aA:CreateEdit(0,20)format_ctrl(aC,20,av,"")aA:CreateOkButton()aA:CreateCancelButton()function callback(aw)end;aA:RegisterHandleCommand(callback)if aA:ExecuteModal(nil)==finale.EXECMODAL_OK then at.LuaString=aC:GetText(at)return at.LuaString end end;function O.is_finale_object(aD)return aD and type(aD)=="userdata"and aD.ClassName and aD.GetClassID and true or false end;function O.system_indent_set_to_prefs(ab,ai)ai=ai or O.get_page_format_prefs()local aE=finale.FCMeasure()local aF=ab.FirstMeasure==1;if not aF and aE:Load(ab.FirstMeasure)then if aE.ShowFullNames then aF=true end end;if aF and ai.UseFirstSystemMargins then ab.LeftMargin=ai.FirstSystemLeft else ab.LeftMargin=ai.SystemLeft end;return ab:Save()end;return O end)c("library.note_entry",function(require,n,c,d)local O={}function O.finale_version(P,Q,R)local S=bit32.bor(bit32.lshift(math.floor(P),24),bit32.lshift(math.floor(Q),20))if R then S=bit32.bor(S,math.floor(R))end;return S end;function O.group_overlaps_region(T,U)if U:IsFullDocumentSpan()then return true end;local V=false;local W=finale.FCSystemStaves()W:LoadAllForRegion(U)for X in each(W)do if T:ContainsStaff(X:GetStaff())then V=true;break end end;if not V then return false end;if T.StartMeasure>U.EndMeasure or T.EndMeasure0 then z=true;break end;a1=a1+1 end;if z then local a3=finale.FCStaffSystem()a3:Load(a2:GetFirstSystem())return finale.FCCell(a3.FirstMeasure,a3.TopStaff)end;local a4=finale.FCMusicRegion()a4:SetFullDocument()return finale.FCCell(a4.EndMeasure,a4.EndStaff)end;function O.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local a5=finale.FCMusicRegion()a5:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),a5.StartStaff)end;return O.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function O.get_top_left_selected_or_visible_cell()local _=finenv.Region()if not _:IsEmpty()then return finale.FCCell(_.StartMeasure,_.StartStaff)end;return O.get_top_left_visible_cell()end;function O.is_default_measure_number_visible_on_cell(a6,a7,a8,a9)local aa=finale.FCCurrentStaffSpec()if not aa:LoadForCell(a7,0)then return false end;if a6:GetShowOnTopStaff()and a7.Staff==a8.TopStaff then return true end;if a6:GetShowOnBottomStaff()and a7.Staff==a8:CalcBottomStaff()then return true end;if aa.ShowMeasureNumbers then return not a6:GetExcludeOtherStaves(a9)end;return false end;function O.is_default_number_visible_and_left_aligned(a6,a7,ab,a9,ac)if a6.UseScoreInfoForParts then a9=false end;if ac and a6:GetShowOnMultiMeasureRests(a9)then if finale.MNALIGN_LEFT~=a6:GetMultiMeasureAlignment(a9)then return false end elseif a7.Measure==ab.FirstMeasure then if not a6:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=a6:GetStartAlignment(a9)then return false end else if not a6:GetShowMultiples(a9)then return false end;if finale.MNALIGN_LEFT~=a6:GetMultipleAlignment(a9)then return false end end;return O.is_default_measure_number_visible_on_cell(a6,a7,ab,a9)end;function O.update_layout(ad,ae)ad=ad or 1;ae=ae or false;local af=finale.FCPage()if af:Load(ad)then af:UpdateLayout(ae)end end;function O.get_current_part()local ag=finale.FCParts()ag:LoadAll()return ag:GetCurrent()end;function O.get_page_format_prefs()local ah=O.get_current_part()local ai=finale.FCPageFormatPrefs()local t=false;if ah:IsScore()then t=ai:LoadScore()else t=ai:LoadParts()end;return ai,t end;function O.get_smufl_metadata_file(aj)if not aj then aj=finale.FCFontInfo()aj:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local ak=function(al,aj)local am=al.."/SMuFL/Fonts/"..aj.Name.."/"..aj.Name..".json"return io.open(am,"r")end;local an=""if finenv.UI():IsOnWindows()then an=os.getenv("LOCALAPPDATA")else an=os.getenv("HOME").."/Library/Application Support"end;local ao=ak(an,aj)if nil~=ao then return ao end;local ap="/Library/Application Support"if finenv.UI():IsOnWindows()then ap=os.getenv("COMMONPROGRAMFILES")end;return ak(ap,aj)end;function O.is_font_smufl_font(aj)if not aj then aj=finale.FCFontInfo()aj:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=O.finale_version(27,1)then if nil~=aj.IsSMuFLFont then return aj.IsSMuFLFont end end;local aq=O.get_smufl_metadata_file(aj)if nil~=aq then io.close(aq)return true end;return false end;function O.simple_input(ar,as)local at=finale.FCString()at.LuaString=""local au=finale.FCString()local av=160;function format_ctrl(aw,ax,ay,az)aw:SetHeight(ax)aw:SetWidth(ay)au.LuaString=az;aw:SetText(au)end;title_width=string.len(ar)*6+54;if title_width>av then av=title_width end;text_width=string.len(as)*6;if text_width>av then av=text_width end;au.LuaString=ar;local aA=finale.FCCustomLuaWindow()aA:SetTitle(au)local aB=aA:CreateStatic(0,0)format_ctrl(aB,16,av,as)local aC=aA:CreateEdit(0,20)format_ctrl(aC,20,av,"")aA:CreateOkButton()aA:CreateCancelButton()function callback(aw)end;aA:RegisterHandleCommand(callback)if aA:ExecuteModal(nil)==finale.EXECMODAL_OK then at.LuaString=aC:GetText(at)return at.LuaString end end;function O.is_finale_object(aD)return aD and type(aD)=="userdata"and aD.ClassName and aD.GetClassID and true or false end;function O.system_indent_set_to_prefs(ab,ai)ai=ai or O.get_page_format_prefs()local aE=finale.FCMeasure()local aF=ab.FirstMeasure==1;if not aF and aE:Load(ab.FirstMeasure)then if aE.ShowFullNames then aF=true end end;if aF and ai.UseFirstSystemMargins then ab.LeftMargin=ai.FirstSystemLeft else ab.LeftMargin=ai.SystemLeft end;return ab:Save()end;return O end)return a("__root") \ No newline at end of file diff --git a/dist/articulation_delete_duplicates.lua b/dist/articulation_delete_duplicates.lua index 55b23288..173d376e 100644 --- a/dist/articulation_delete_duplicates.lua +++ b/dist/articulation_delete_duplicates.lua @@ -1,38 +1 @@ -function plugindef() - finaleplugin.Author = "CJ Garcia" - finaleplugin.Copyright = "© 2020 CJ Garcia Music" - finaleplugin.Version = "1.0" - finaleplugin.Date = "June 22, 2020" - finaleplugin.CategoryTags = "Articulation" - return "Remove Duplicate Articulations", "Remove Duplicate Articulations", "Remove Duplicate Articulations" -end - -function articulation_delete_duplicates() - for note_entry in eachentrysaved(finenv.Region()) do - local art_list = {} - local arts = note_entry:CreateArticulations() - for a in each(arts) do - table.insert(art_list, a:GetID()) - end - local sort_list = {} - local unique_list = {} - for k,v in ipairs(art_list) do - if (not sort_list[v]) then - unique_list[#unique_list + 1] = v - sort_list[v] = true - end - end - for key, value in pairs(art_list) do - for a in each(arts) do - a:DeleteData() - end - end - for key, value in pairs(unique_list) do - local art = finale.FCArticulation() - art:SetNoteEntry(note_entry) - art:SetID(value) art:SaveNew() - end - end -end - -articulation_delete_duplicates() +function plugindef()finaleplugin.Author="CJ Garcia"finaleplugin.Copyright="© 2020 CJ Garcia Music"finaleplugin.Version="1.0"finaleplugin.Date="June 22, 2020"finaleplugin.CategoryTags="Articulation"return"Remove Duplicate Articulations","Remove Duplicate Articulations","Remove Duplicate Articulations"end;function articulation_delete_duplicates()for a in eachentrysaved(finenv.Region())do local b={}local c=a:CreateArticulations()for d in each(c)do table.insert(b,d:GetID())end;local e={}local f={}for g,h in ipairs(b)do if not e[h]then f[#f+1]=h;e[h]=true end end;for i,j in pairs(b)do for d in each(c)do d:DeleteData()end end;for i,j in pairs(f)do local k=finale.FCArticulation()k:SetNoteEntry(a)k:SetID(j)k:SaveNew()end end end;articulation_delete_duplicates() \ No newline at end of file diff --git a/dist/articulation_remove_from_rests.lua b/dist/articulation_remove_from_rests.lua index 00319f4d..9b8e6e59 100644 --- a/dist/articulation_remove_from_rests.lua +++ b/dist/articulation_remove_from_rests.lua @@ -1,32 +1 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0" - finaleplugin.Date = "June 19, 2020" - finaleplugin.CategoryTags = "Articulation" - finaleplugin.AuthorURL = "https://nickmazuk.com" - return "Remove Articulations from Rests", "Remove Articulations from Rests", - "If a rest has an articulation, it removes it (except breath marks, caesuras, or fermatas" -end - -function articulation_remove_from_rests() - for entry in eachentrysaved(finenv.Region()) do - if entry:IsRest() and entry:GetArticulationFlag() then - local a = finale.FCArticulation() - a:SetNoteEntry(entry) - if a:LoadFirst() then - local ad = finale.FCArticulationDef() - if ad:Load(a:GetID()) then - local char = ad:GetAboveSymbolChar() - print(char) - if char ~= 85 and char ~= 34 and char ~= 44 then - entry:SetArticulationFlag(false) - end - end - end - end - end -end - -articulation_remove_from_rests() +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Nick Mazuk"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0"finaleplugin.Date="June 19, 2020"finaleplugin.CategoryTags="Articulation"finaleplugin.AuthorURL="https://nickmazuk.com"return"Remove Articulations from Rests","Remove Articulations from Rests","If a rest has an articulation, it removes it (except breath marks, caesuras, or fermatas"end;function articulation_remove_from_rests()for a in eachentrysaved(finenv.Region())do if a:IsRest()and a:GetArticulationFlag()then local b=finale.FCArticulation()b:SetNoteEntry(a)if b:LoadFirst()then local c=finale.FCArticulationDef()if c:Load(b:GetID())then local d=c:GetAboveSymbolChar()print(d)if d~=85 and d~=34 and d~=44 then a:SetArticulationFlag(false)end end end end end end;articulation_remove_from_rests() \ No newline at end of file diff --git a/dist/articulation_reset_auto_positioning.lua b/dist/articulation_reset_auto_positioning.lua index 4e189fb4..2952978f 100644 --- a/dist/articulation_reset_auto_positioning.lua +++ b/dist/articulation_reset_auto_positioning.lua @@ -1,50 +1,6 @@ -function plugindef() - finaleplugin.Author = "Robert Patterson" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0" - finaleplugin.Date = "February 28, 2020" - finaleplugin.CategoryTags = "Articulation" - finaleplugin.MinFinaleVersionRaw = 0x1a000000 - finaleplugin.MinJWLuaVersion = 0.58 - finaleplugin.Notes = [[ +function plugindef()finaleplugin.Author="Robert Patterson"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0"finaleplugin.Date="February 28, 2020"finaleplugin.CategoryTags="Articulation"finaleplugin.MinFinaleVersionRaw=0x1a000000;finaleplugin.MinJWLuaVersion=0.58;finaleplugin.Notes=[[ This script resets all selected articulations to their default positions but only if they are not manually positioned. Due to complications arising from how Finale stored articulation positions before Finale 26, it requires Finale 26 or higher. Due to issues around maintaining the context for automatic stacking, it must be run under RGP Lua. JW Lua does not have the necessary logic to manage the stacking context. - ]] - return "Reset Automatic Articulation Positions", "Reset Automatic Articulation Positions", "Resets the position of automatically positioned articulations while ignoring those with manual positioning." -end - --- Before Finale 26, the automatic positioning of articulations was calculated by Finale and stored as the default offset --- values of the assignment. Starting with Finale 26, the automatic positioning of articulations is inherent in the --- coded behavior of Finale. The assignment only contains offsets from the default position. Therefore, resetting --- articulations positions in earlier versions would require reverse-engineering all the automatic positioning --- options. But resetting articulations to default in Finale 26 and higher is a simple matter of zeroing out --- the horizontal and/or vertical offsets. - -function articulation_reset_auto_positioning() - for note_entry in eachentry(finenv.Region()) do - local articulations = note_entry:CreateArticulations() - for articulation in each(articulations) do - local articulation_def = finale.FCArticulationDef() - if articulation_def:Load(articulation.ID) then - local do_save = false - if articulation_def.CenterHorizontally then - articulation.HorizontalPos = 0 - do_save = true - end - if finale.ARTPOS_MANUAL_POSITIONING ~= articulation_def.AutoPosSide then - local save_horzpos = articulation.HorizontalPos - articulation:ResetPos(articulation_def) -- use ResetPos to fix up Finale's internal stacking flags - articulation.HorizontalPos = save_horzpos - do_save = true - end - if do_save then - articulation:Save() - end - end - end - end -end - -articulation_reset_auto_positioning() + ]]return"Reset Automatic Articulation Positions","Reset Automatic Articulation Positions","Resets the position of automatically positioned articulations while ignoring those with manual positioning."end;function articulation_reset_auto_positioning()for a in eachentry(finenv.Region())do local b=a:CreateArticulations()for c in each(b)do local d=finale.FCArticulationDef()if d:Load(c.ID)then local e=false;if d.CenterHorizontally then c.HorizontalPos=0;e=true end;if finale.ARTPOS_MANUAL_POSITIONING~=d.AutoPosSide then local f=c.HorizontalPos;c:ResetPos(d)c.HorizontalPos=f;e=true end;if e then c:Save()end end end end end;articulation_reset_auto_positioning() \ No newline at end of file diff --git a/dist/articulation_reset_positioning.lua b/dist/articulation_reset_positioning.lua index c8cd3c0e..52821a74 100644 --- a/dist/articulation_reset_positioning.lua +++ b/dist/articulation_reset_positioning.lua @@ -1,37 +1,6 @@ -function plugindef() - finaleplugin.Author = "Robert Patterson" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0" - finaleplugin.Date = "February 28, 2020" - finaleplugin.CategoryTags = "Articulation" - finaleplugin.MinFinaleVersionRaw = 0x1a000000 - finaleplugin.MinJWLuaVersion = 0.58 - finaleplugin.Notes = [[ +function plugindef()finaleplugin.Author="Robert Patterson"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0"finaleplugin.Date="February 28, 2020"finaleplugin.CategoryTags="Articulation"finaleplugin.MinFinaleVersionRaw=0x1a000000;finaleplugin.MinJWLuaVersion=0.58;finaleplugin.Notes=[[ This script resets all selected articulations to their default positions. Due to complications arising from how Finale stored articulation positions before Finale 26, it requires Finale 26 or higher. Due to issues around maintaining the context for automatic stacking, it must be run under RGP Lua. JW Lua does not have the necessary logic to manage the stacking context. - ]] - return "Reset Articulation Positions", "Reset Articulation Positions", "Resets the position of all selected articulations." -end - --- Before Finale 26, the automatic positioning of articulations was calculated by Finale and stored as the default offset --- values of the assignment. Starting with Finale 26, the automatic positioning of articulations is inherent in the --- coded behavior of Finale. The assignment only contains offsets from the default position. Therefore, resetting --- articulations positions in earlier versions would require reverse-engineering all the automatic positioning --- options. But resetting articulations to default in Finale 26 and higher is a simple matter of zeroing out --- the horizontal and/or vertical offsets. However, some additional logic is required to maintain stacking flags, --- so this script should only be run under RGP Lua, never JW Lua. - -function articulation_reset_positioning() - for note_entry in eachentry(finenv.Region()) do - local articulations = note_entry:CreateArticulations() - for articulation in each(articulations) do - local artic_def = articulation:CreateArticulationDef() - articulation:ResetPos(artic_def) - articulation:Save() - end - end -end - -articulation_reset_positioning() + ]]return"Reset Articulation Positions","Reset Articulation Positions","Resets the position of all selected articulations."end;function articulation_reset_positioning()for a in eachentry(finenv.Region())do local b=a:CreateArticulations()for c in each(b)do local d=c:CreateArticulationDef()c:ResetPos(d)c:Save()end end end;articulation_reset_positioning() \ No newline at end of file diff --git a/dist/barline_set.lua b/dist/barline_set.lua index af6401a9..4568dec8 100644 --- a/dist/barline_set.lua +++ b/dist/barline_set.lua @@ -1,30 +1,19 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Author = "Carl Vine" - finaleplugin.AuthorURL = "http://carlvine.com/?cv=lua" - finaleplugin.Version = "v1.2" - finaleplugin.Date = "2022/06/18" - finaleplugin.MinJWLuaVersion = 0.62 - finaleplugin.AdditionalMenuOptions = [[ +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Author="Carl Vine"finaleplugin.AuthorURL="http://carlvine.com/?cv=lua"finaleplugin.Version="v1.2"finaleplugin.Date="2022/06/18"finaleplugin.MinJWLuaVersion=0.62;finaleplugin.AdditionalMenuOptions=[[ Barline Set Double Barline Set Final Barline Set None Barline Set Dashed - ]] - finaleplugin.AdditionalUndoText = [[ + ]]finaleplugin.AdditionalUndoText=[[ Barline Set Double Barline Set Final Barline Set None Barline Set Dashed - ]] - finaleplugin.AdditionalPrefixes = [[ + ]]finaleplugin.AdditionalPrefixes=[[ new_barline = finale.BARLINE_DOUBLE new_barline = finale.BARLINE_FINAL new_barline = finale.BARLINE_NONE new_barline = finale.BARLINE_DASHED - ]] - finaleplugin.Notes = [[ + ]]finaleplugin.Notes=[[ Change all selected barlines to the normal "single" barline. Under RGPLua (0.62 and above) additional menu items are created offering four other barline types: @@ -34,24 +23,4 @@ function plugindef() Barline Set None Barline Set Dashed ``` - ]] - return "Barline Set Normal", "Barline Set Normal", "Set barlines to one of five styles" -end - --- default to "SINGLE" barline for "normal" operation -new_barline = new_barline or finale.BARLINE_NORMAL - -function change_barline() - local region = finenv.Region() - region.StartMeasurePos = 0 - region:SetEndMeasurePosRight() - - local measures = finale.FCMeasures() - measures:LoadRegion(region) - for measure in each(measures) do - measure.Barline = new_barline - end - measures:SaveAll() -end - -change_barline() + ]]return"Barline Set Normal","Barline Set Normal","Set barlines to one of five styles"end;new_barline=new_barline or finale.BARLINE_NORMAL;function change_barline()local a=finenv.Region()a.StartMeasurePos=0;a:SetEndMeasurePosRight()local b=finale.FCMeasures()b:LoadRegion(a)for c in each(b)do c.Barline=new_barline end;b:SaveAll()end;change_barline() \ No newline at end of file diff --git a/dist/baseline_move_reset.lua b/dist/baseline_move_reset.lua index b111444d..7479f48f 100644 --- a/dist/baseline_move_reset.lua +++ b/dist/baseline_move_reset.lua @@ -1,13 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Version = "1.0" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "May 15, 2022" - finaleplugin.CategoryTags = "Baseline" - finaleplugin.AuthorURL = "http://robertgpatterson.com" - finaleplugin.MinJWLuaVersion = 0.62 - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Robert Patterson"finaleplugin.Version="1.0"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Date="May 15, 2022"finaleplugin.CategoryTags="Baseline"finaleplugin.AuthorURL="http://robertgpatterson.com"finaleplugin.MinJWLuaVersion=0.62;finaleplugin.Notes=[[ This script nudges system baselines up or down by a single staff-space (24 evpus). It introduces 10 menu options to nudge each baseline type up or down. It also introduces 5 menu options to reset the baselines to their staff-level values. @@ -28,8 +19,7 @@ function plugindef() ``` A value in a prefix overrides any setting in a configuration file. - ]] - finaleplugin.AdditionalMenuOptions = [[ + ]]finaleplugin.AdditionalMenuOptions=[[ Move Lyric Baselines Up Reset Lyric Baselines Move Expression Baseline Above Down @@ -44,8 +34,7 @@ function plugindef() Move Fretboard Baseline Down Move Fretboard Baseline Up Reset Fretboard Baseline - ]] - finaleplugin.AdditionalDescriptions = [[ + ]]finaleplugin.AdditionalDescriptions=[[ Moves all lyrics baselines up one space in the selected systems Resets all selected lyrics baselines to default Moves the selected expression above baseline down one space @@ -60,8 +49,7 @@ function plugindef() Moves the selected fretboard baseline down one space Moves the selected fretboard baseline up one space Resets the selected fretboard baselines - ]] - finaleplugin.AdditionalPrefixes = [[ + ]]finaleplugin.AdditionalPrefixes=[[ direction = 1 -- no baseline_types table, which picks up the default (lyrics) direction = 0 -- no baseline_types table, which picks up the default (lyrics) direction = -1 baseline_types = {finale.BASELINEMODE_EXPRESSIONABOVE} @@ -76,376 +64,4 @@ function plugindef() direction = -1 baseline_types = {finale.BASELINEMODE_FRETBOARD} direction = 1 baseline_types = {finale.BASELINEMODE_FRETBOARD} direction = 0 baseline_types = {finale.BASELINEMODE_FRETBOARD} - ]] - return "Move Lyric Baselines Down", "Move Lyrics Baselines Down", "Moves all lyrics baselines down one space in the selected systems" -end - --- Author: Robert Patterson --- Date: March 5, 2021 ---[[ -$module Configuration - -This library implements a UTF-8 text file scheme for configuration and user settings as follows: - -- Comments start with `--` -- Leading, trailing, and extra whitespace is ignored -- Each parameter is named and delimited as follows: - -``` - = -``` - -Parameter values may be: - -- Strings delimited with either single- or double-quotes -- Tables delimited with `{}` that may contain strings, booleans, or numbers -- Booleans (`true` or `false`) -- Numbers - -Currently the following are not supported: - -- Tables embedded within tables -- Tables containing strings that contain commas - -A sample configuration file might be: - -```lua --- Configuration File for "Hairpin and Dynamic Adjustments" script --- -left_dynamic_cushion = 12 --evpus -right_dynamic_cushion = -6 --evpus -``` - -## Configuration Files - -Configuration files provide a way for power users to modify script behavior without -having to modify the script itself. Some users track their changes to their configuration files, -so scripts should not create or modify them programmatically. - -- The user creates each configuration file in a subfolder called `script_settings` within -the folder of the calling script. -- Each script that has a configuration file defines its own configuration file name. -- It is entirely appropriate over time for scripts to transition from configuration files to user settings, -but this requires implementing a user interface to modify the user settings from within the script. -(See below.) - -## User Settings Files - -User settings are written by the scripts themselves and reside in the user's preferences folder -in an appropriately-named location for the operating system. (The naming convention is a detail that the -configuration library handles for the caller.) If the user settings are to be changed from their defaults, -the script itself should provide a means to change them. This could be a (preferably optional) dialog box -or any other mechanism the script author chooses. - -User settings are saved in the user's preferences folder (on Mac) or AppData folder (on Windows). - -## Merge Process - -Files are _merged_ into the passed-in list of default values. They do not _replace_ the list. Each calling script contains -a table of all the configurable parameters or settings it recognizes along with default values. An example: - -`sample.lua:` - -```lua -parameters = { - x = 1, - y = 2, - z = 3 -} - -configuration.get_parameters(parameters, "script.config.txt") - -for k, v in pairs(parameters) do - print(k, v) -end -``` - -Suppose the `script.config.text` file is as follows: - -``` -y = 4 -q = 6 -``` - -The returned parameters list is: - - -```lua -parameters = { - x = 1, -- remains the default value passed in - y = 4, -- replaced value from the config file - z = 3 -- remains the default value passed in -} -``` - -The `q` parameter in the config file is ignored because the input paramater list -had no `q` parameter. - -This approach allows total flexibility for the script add to or modify its list of parameters -without having to worry about older configuration files or user settings affecting it. -]] - -local configuration = {} - -local script_settings_dir = "script_settings" -- the parent of this directory is the running lua path -local comment_marker = "--" -local parameter_delimiter = "=" -local path_delimiter = "/" - -local file_exists = function(file_path) - local f = io.open(file_path, "r") - if nil ~= f then - io.close(f) - return true - end - return false -end - -local strip_leading_trailing_whitespace = function(str) - return str:match("^%s*(.-)%s*$") -- lua pattern magic taken from the Internet -end - -local parse_table = function(val_string) - local ret_table = {} - for element in val_string:gmatch("[^,%s]+") do -- lua pattern magic taken from the Internet - local parsed_element = parse_parameter(element) - table.insert(ret_table, parsed_element) - end - return ret_table -end - -parse_parameter = function(val_string) - if "\"" == val_string:sub(1, 1) and "\"" == val_string:sub(#val_string, #val_string) then -- double-quote string - return string.gsub(val_string, "\"(.+)\"", "%1") -- lua pattern magic: "(.+)" matches all characters between two double-quote marks (no escape chars) - elseif "'" == val_string:sub(1, 1) and "'" == val_string:sub(#val_string, #val_string) then -- single-quote string - return string.gsub(val_string, "'(.+)'", "%1") -- lua pattern magic: '(.+)' matches all characters between two single-quote marks (no escape chars) - elseif "{" == val_string:sub(1, 1) and "}" == val_string:sub(#val_string, #val_string) then - return parse_table(string.gsub(val_string, "{(.+)}", "%1")) - elseif "true" == val_string then - return true - elseif "false" == val_string then - return false - end - return tonumber(val_string) -end - -local get_parameters_from_file = function(file_path, parameter_list) - local file_parameters = {} - - if not file_exists(file_path) then - return false - end - - for line in io.lines(file_path) do - local comment_at = string.find(line, comment_marker, 1, true) -- true means find raw string rather than lua pattern - if nil ~= comment_at then - line = string.sub(line, 1, comment_at - 1) - end - local delimiter_at = string.find(line, parameter_delimiter, 1, true) - if nil ~= delimiter_at then - local name = strip_leading_trailing_whitespace(string.sub(line, 1, delimiter_at - 1)) - local val_string = strip_leading_trailing_whitespace(string.sub(line, delimiter_at + 1)) - file_parameters[name] = parse_parameter(val_string) - end - end - - for param_name, _ in pairs(parameter_list) do - local param_val = file_parameters[param_name] - if nil ~= param_val then - parameter_list[param_name] = param_val - end - end - - return true -end - ---[[ -% get_parameters - -Searches for a file with the input filename in the `script_settings` directory and replaces the default values in `parameter_list` -with any that are found in the config file. - -@ file_name (string) the file name of the config file (which will be prepended with the `script_settings` directory) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true if the file exists -]] -function configuration.get_parameters(file_name, parameter_list) - local path = "" - if finenv.IsRGPLua then - path = finenv.RunningLuaFolderPath() - else - local str = finale.FCString() - str:SetRunningLuaFolderPath() - path = str.LuaString - end - local file_path = path .. script_settings_dir .. path_delimiter .. file_name - return get_parameters_from_file(file_path, parameter_list) -end - --- Calculates a filepath in the user's preferences folder using recommended naming conventions --- -local calc_preferences_filepath = function(script_name) - local str = finale.FCString() - str:SetUserOptionsPath() - local folder_name = str.LuaString - if not finenv.IsRGPLua and finenv.UI():IsOnMac() then - -- works around bug in SetUserOptionsPath() in JW Lua - folder_name = os.getenv("HOME") .. folder_name:sub(2) -- strip '~' and replace with actual folder - end - if finenv.UI():IsOnWindows() then - folder_name = folder_name .. path_delimiter .. "FinaleLua" - end - local file_path = folder_name .. path_delimiter - if finenv.UI():IsOnMac() then - file_path = file_path .. "com.finalelua." - end - file_path = file_path .. script_name .. ".settings.txt" - return file_path, folder_name -end - ---[[ -% save_user_settings - -Saves the user's preferences for a script from the values provided in `parameter_list`. - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true on success -]] -function configuration.save_user_settings(script_name, parameter_list) - local file_path, folder_path = calc_preferences_filepath(script_name) - local file = io.open(file_path, "w") - if not file and finenv.UI():IsOnWindows() then -- file not found - os.execute('mkdir "' .. folder_path ..'"') -- so try to make a folder (windows only, since the folder is guaranteed to exist on mac) - file = io.open(file_path, "w") -- try the file again - end - if not file then -- still couldn't find file - return false -- so give up - end - file:write("-- User settings for " .. script_name .. ".lua\n\n") - for k,v in pairs(parameter_list) do -- only number, boolean, or string values - if type(v) == "string" then - v = "\"" .. v .."\"" - else - v = tostring(v) - end - file:write(k, " = ", v, "\n") - end - file:close() - return true -- success -end - ---[[ -% get_user_settings - -Find the user's settings for a script in the preferences directory and replaces the default values in `parameter_list` -with any that are found in the preferences file. The actual name and path of the preferences file is OS dependent, so -the input string should just be the script name (without an extension). - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -@ [create_automatically] (boolean) if true, create the file automatically (default is `true`) -: (boolean) `true` if the file already existed, `false` if it did not or if it was created automatically -]] -function configuration.get_user_settings(script_name, parameter_list, create_automatically) - if create_automatically == nil then create_automatically = true end - local exists = get_parameters_from_file(calc_preferences_filepath(script_name), parameter_list) - if not exists and create_automatically then - configuration.save_user_settings(script_name, parameter_list) - end - return exists -end - - - - -local config = {nudge_evpus = 24} - -if nil ~= configuration then - configuration.get_parameters("baseline_move.config.txt", config) -end - -local lyric_baseline_types = { - [finale.BASELINEMODE_LYRICSVERSE] = function() - return finale.FCVerseLyricsText() - end, - [finale.BASELINEMODE_LYRICSCHORUS] = function() - return finale.FCChorusLyricsText() - end, - [finale.BASELINEMODE_LYRICSSECTION] = function() - return finale.FCSectionLyricsText() - end, -} - -local find_valid_lyric_nums = function(baseline_type) - local lyrics_text_class_constructor = lyric_baseline_types[baseline_type] - if lyrics_text_class_constructor then - local valid_lyric_nums = {} - local lyrics_text_class = lyrics_text_class_constructor() - for i = 1, 32767, 1 do - if lyrics_text_class:Load(i) then - local str = finale.FCString() - lyrics_text_class:GetText(str) - if not str:IsEmpty() then - valid_lyric_nums[{baseline_type, i}] = 1 - end - end - end - return valid_lyric_nums - end - return nil -end - -function baseline_move() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - for _, baseline_type in pairs(baseline_types) do - local valid_lyric_nums = find_valid_lyric_nums(baseline_type) -- will be nil for non-lyric baseline types - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - if direction ~= 0 then - baselines:LoadAllForSystem(baseline_type, i) - for j = start_slot, end_slot do - if valid_lyric_nums then - for lyric_info, _ in pairs(valid_lyric_nums) do - local _, lyric_number = table.unpack(lyric_info) - bl = baselines:AssureSavedLyricNumber(baseline_type, i, region:CalcStaffNumber(j), lyric_number) - bl.VerticalOffset = bl.VerticalOffset + direction * nudge_evpus - bl:Save() - end - else - bl = baselines:AssureSavedStaff(baseline_type, i, region:CalcStaffNumber(j)) - bl.VerticalOffset = bl.VerticalOffset + direction * nudge_evpus - bl:Save() - end - end - else - for j = start_slot, end_slot do - baselines:LoadAllForSystemStaff(baseline_type, i, region:CalcStaffNumber(j)) - -- iterate backwards to preserve lower inci numbers when deleting - for baseline in eachbackwards(baselines) do - baseline:DeleteData() - end - end - end - end - end -end - --- parameters for additional menu options -baseline_types = baseline_types or {finale.BASELINEMODE_LYRICSVERSE, finale.BASELINEMODE_LYRICSCHORUS, finale.BASELINEMODE_LYRICSSECTION} -direction = direction or -1 -nudge_evpus = nudge_evpus or config.nudge_evpus - -baseline_move() + ]]return"Move Lyric Baselines Down","Move Lyrics Baselines Down","Moves all lyrics baselines down one space in the selected systems"end;local o=require("library.configuration")local p={nudge_evpus=24}if nil~=o then o.get_parameters("baseline_move.config.txt",p)end;local q={[finale.BASELINEMODE_LYRICSVERSE]=function()return finale.FCVerseLyricsText()end,[finale.BASELINEMODE_LYRICSCHORUS]=function()return finale.FCChorusLyricsText()end,[finale.BASELINEMODE_LYRICSSECTION]=function()return finale.FCSectionLyricsText()end}local r=function(s)local t=q[s]if t then local u={}local v=t()for w=1,32767,1 do if v:Load(w)then local x=finale.FCString()v:GetText(x)if not x:IsEmpty()then u[{s,w}]=1 end end end;return u end;return nil end;function baseline_move()local y=finenv.Region()local z=finale.FCStaffSystems()z:LoadAll()local A=y:GetStartMeasure()local B=y:GetEndMeasure()local C=z:FindMeasureNumber(A)local D=z:FindMeasureNumber(B)local E=C:GetItemNo()local F=D:GetItemNo()local G=y:GetStartSlot()local H=y:GetEndSlot()for I,s in pairs(baseline_types)do local u=r(s)for w=E,F,1 do local J=finale.FCBaselines()if direction~=0 then J:LoadAllForSystem(s,w)for K=G,H do if u then for L,I in pairs(u)do local I,M=table.unpack(L)bl=J:AssureSavedLyricNumber(s,w,y:CalcStaffNumber(K),M)bl.VerticalOffset=bl.VerticalOffset+direction*nudge_evpus;bl:Save()end else bl=J:AssureSavedStaff(s,w,y:CalcStaffNumber(K))bl.VerticalOffset=bl.VerticalOffset+direction*nudge_evpus;bl:Save()end end else for K=G,H do J:LoadAllForSystemStaff(s,w,y:CalcStaffNumber(K))for N in eachbackwards(J)do N:DeleteData()end end end end end end;baseline_types=baseline_types or{finale.BASELINEMODE_LYRICSVERSE,finale.BASELINEMODE_LYRICSCHORUS,finale.BASELINEMODE_LYRICSSECTION}direction=direction or-1;nudge_evpus=nudge_evpus or p.nudge_evpus;baseline_move()end)c("library.configuration",function(require,n,c,d)local O={}function O.finale_version(P,Q,R)local S=bit32.bor(bit32.lshift(math.floor(P),24),bit32.lshift(math.floor(Q),20))if R then S=bit32.bor(S,math.floor(R))end;return S end;function O.group_overlaps_region(T,y)if y:IsFullDocumentSpan()then return true end;local U=false;local V=finale.FCSystemStaves()V:LoadAllForRegion(y)for W in each(V)do if T:ContainsStaff(W:GetStaff())then U=true;break end end;if not U then return false end;if T.StartMeasure>y.EndMeasure or T.EndMeasure0 then a2=true;break end;a0=a0+1 end;if a2 then local a3=finale.FCStaffSystem()a3:Load(a1:GetFirstSystem())return finale.FCCell(a3.FirstMeasure,a3.TopStaff)end;local a4=finale.FCMusicRegion()a4:SetFullDocument()return finale.FCCell(a4.EndMeasure,a4.EndStaff)end;function O.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local a5=finale.FCMusicRegion()a5:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),a5.StartStaff)end;return O.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function O.get_top_left_selected_or_visible_cell()local Z=finenv.Region()if not Z:IsEmpty()then return finale.FCCell(Z.StartMeasure,Z.StartStaff)end;return O.get_top_left_visible_cell()end;function O.is_default_measure_number_visible_on_cell(a6,a7,a8,a9)local aa=finale.FCCurrentStaffSpec()if not aa:LoadForCell(a7,0)then return false end;if a6:GetShowOnTopStaff()and a7.Staff==a8.TopStaff then return true end;if a6:GetShowOnBottomStaff()and a7.Staff==a8:CalcBottomStaff()then return true end;if aa.ShowMeasureNumbers then return not a6:GetExcludeOtherStaves(a9)end;return false end;function O.is_default_number_visible_and_left_aligned(a6,a7,C,a9,ab)if a6.UseScoreInfoForParts then a9=false end;if ab and a6:GetShowOnMultiMeasureRests(a9)then if finale.MNALIGN_LEFT~=a6:GetMultiMeasureAlignment(a9)then return false end elseif a7.Measure==C.FirstMeasure then if not a6:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=a6:GetStartAlignment(a9)then return false end else if not a6:GetShowMultiples(a9)then return false end;if finale.MNALIGN_LEFT~=a6:GetMultipleAlignment(a9)then return false end end;return O.is_default_measure_number_visible_on_cell(a6,a7,C,a9)end;function O.update_layout(ac,ad)ac=ac or 1;ad=ad or false;local ae=finale.FCPage()if ae:Load(ac)then ae:UpdateLayout(ad)end end;function O.get_current_part()local af=finale.FCParts()af:LoadAll()return af:GetCurrent()end;function O.get_page_format_prefs()local ag=O.get_current_part()local ah=finale.FCPageFormatPrefs()local ai=false;if ag:IsScore()then ai=ah:LoadScore()else ai=ah:LoadParts()end;return ah,ai end;function O.get_smufl_metadata_file(aj)if not aj then aj=finale.FCFontInfo()aj:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local ak=function(al,aj)local am=al.."/SMuFL/Fonts/"..aj.Name.."/"..aj.Name..".json"return io.open(am,"r")end;local an=""if finenv.UI():IsOnWindows()then an=os.getenv("LOCALAPPDATA")else an=os.getenv("HOME").."/Library/Application Support"end;local ao=ak(an,aj)if nil~=ao then return ao end;local ap="/Library/Application Support"if finenv.UI():IsOnWindows()then ap=os.getenv("COMMONPROGRAMFILES")end;return ak(ap,aj)end;function O.is_font_smufl_font(aj)if not aj then aj=finale.FCFontInfo()aj:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=O.finale_version(27,1)then if nil~=aj.IsSMuFLFont then return aj.IsSMuFLFont end end;local aq=O.get_smufl_metadata_file(aj)if nil~=aq then io.close(aq)return true end;return false end;function O.simple_input(ar,as)local at=finale.FCString()at.LuaString=""local x=finale.FCString()local au=160;function format_ctrl(av,aw,ax,ay)av:SetHeight(aw)av:SetWidth(ax)x.LuaString=ay;av:SetText(x)end;title_width=string.len(ar)*6+54;if title_width>au then au=title_width end;text_width=string.len(as)*6;if text_width>au then au=text_width end;x.LuaString=ar;local az=finale.FCCustomLuaWindow()az:SetTitle(x)local aA=az:CreateStatic(0,0)format_ctrl(aA,16,au,as)local aB=az:CreateEdit(0,20)format_ctrl(aB,20,au,"")az:CreateOkButton()az:CreateCancelButton()function callback(av)end;az:RegisterHandleCommand(callback)if az:ExecuteModal(nil)==finale.EXECMODAL_OK then at.LuaString=aB:GetText(at)return at.LuaString end end;function O.is_finale_object(aC)return aC and type(aC)=="userdata"and aC.ClassName and aC.GetClassID and true or false end;function O.system_indent_set_to_prefs(C,ah)ah=ah or O.get_page_format_prefs()local aD=finale.FCMeasure()local aE=C.FirstMeasure==1;if not aE and aD:Load(C.FirstMeasure)then if aD.ShowFullNames then aE=true end end;if aE and ah.UseFirstSystemMargins then C.LeftMargin=ah.FirstSystemLeft else C.LeftMargin=ah.SystemLeft end;return C:Save()end;return O end)return a("__root") \ No newline at end of file diff --git a/dist/beam_selected_region.lua b/dist/beam_selected_region.lua index 1bf65697..b88367e4 100644 --- a/dist/beam_selected_region.lua +++ b/dist/beam_selected_region.lua @@ -1,14 +1,4 @@ -function plugindef() - -- This function and the 'finaleplugin' namespace - -- are both reserved for the plug-in definition. - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "2.0" - finaleplugin.Date = "May 17, 2022" - finaleplugin.CategoryTags = "Note" - finaleplugin.MinJWLuaVersion = 0.62 - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Robert Patterson"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="2.0"finaleplugin.Date="May 17, 2022"finaleplugin.CategoryTags="Note"finaleplugin.MinJWLuaVersion=0.62;finaleplugin.Notes=[[ This script beams together any notes or rests in the selected region that can be beamed together and breaks beams that cross into or out of the selected region at the boundaries of the selected region. The beam options in Finale’s @@ -24,464 +14,10 @@ function plugindef() the Shift key pressed. This is identical to invoking the "Unbeam Selected Region" menu option. This script could be particularly useful if you assign it a keystroke using a keyboard macro utility. - ]] - finaleplugin.AdditionalMenuOptions = [[ + ]]finaleplugin.AdditionalMenuOptions=[[ Unbeam Selected Region - ]] - finaleplugin.AdditionalDescriptions = [[ + ]]finaleplugin.AdditionalDescriptions=[[ Unbeam Selected Region - ]] - finaleplugin.AdditionalPrefixes = [[ + ]]finaleplugin.AdditionalPrefixes=[[ do_unbeam = true - ]] - return "Beam Selected Region", "Beam Selected Region", "Beam Selected Region" -end - ---[[ -$module Note Entry -]] -- -local note_entry = {} - ---[[ -% get_music_region - -Returns an intance of `FCMusicRegion` that corresponds to the metric location of the input note entry. - -@ entry (FCNoteEntry) -: (FCMusicRegion) -]] -function note_entry.get_music_region(entry) - local exp_region = finale.FCMusicRegion() - exp_region:SetCurrentSelection() -- called to match the selected IU list (e.g., if using Staff Sets) - exp_region.StartStaff = entry.Staff - exp_region.EndStaff = entry.Staff - exp_region.StartMeasure = entry.Measure - exp_region.EndMeasure = entry.Measure - exp_region.StartMeasurePos = entry.MeasurePos - exp_region.EndMeasurePos = entry.MeasurePos - return exp_region -end - --- entry_metrics can be omitted, in which case they are constructed and released here --- return entry_metrics, loaded_here -local use_or_get_passed_in_entry_metrics = function(entry, entry_metrics) - if entry_metrics then - return entry_metrics, false - end - entry_metrics = finale.FCEntryMetrics() - if entry_metrics:Load(entry) then - return entry_metrics, true - end - return nil, false -end - ---[[ -% get_evpu_notehead_height - -Returns the calculated height of the notehead rectangle. - -@ entry (FCNoteEntry) - -: (number) the EVPU height -]] -function note_entry.get_evpu_notehead_height(entry) - local highest_note = entry:CalcHighestNote(nil) - local lowest_note = entry:CalcLowestNote(nil) - local evpu_height = (2 + highest_note:CalcStaffPosition() - lowest_note:CalcStaffPosition()) * 12 -- 12 evpu per staff step; add 2 staff steps to accommodate for notehead height at top and bottom - return evpu_height -end - ---[[ -% get_top_note_position - -Returns the vertical page coordinate of the top of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_top_note_position(entry, entry_metrics) - local retval = -math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if not entry:CalcStemUp() then - retval = entry_metrics.TopPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.BottomPosition + scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% get_bottom_note_position - -Returns the vertical page coordinate of the bottom of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_bottom_note_position(entry, entry_metrics) - local retval = math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if entry:CalcStemUp() then - retval = entry_metrics.BottomPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.TopPosition - scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% calc_widths - -Get the widest left-side notehead width and widest right-side notehead width. - -@ entry (FCNoteEntry) -: (number, number) widest left-side notehead width and widest right-side notehead width -]] -function note_entry.calc_widths(entry) - local left_width = 0 - local right_width = 0 - for note in each(entry) do - local note_width = note:CalcNoteheadWidth() - if note_width > 0 then - if note:CalcRightsidePlacement() then - if note_width > right_width then - right_width = note_width - end - else - if note_width > left_width then - left_width = note_width - end - end - end - end - return left_width, right_width -end - --- These functions return the offset for an expression handle. --- Expression handles are vertical when they are left-aligned --- with the primary notehead rectangle. - ---[[ -% calc_left_of_all_noteheads - -Calculates the handle offset for an expression with "Left of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_all_noteheads(entry) - if entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return -left -end - ---[[ -% calc_left_of_primary_notehead - -Calculates the handle offset for an expression with "Left of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_primary_notehead(entry) - return 0 -end - ---[[ -% calc_center_of_all_noteheads - -Calculates the handle offset for an expression with "Center of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - local width_centered = (left + right) / 2 - if not entry:CalcStemUp() then - width_centered = width_centered - left - end - return width_centered -end - ---[[ -% calc_center_of_primary_notehead - -Calculates the handle offset for an expression with "Center of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_primary_notehead(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left / 2 - end - return right / 2 -end - ---[[ -% calc_stem_offset - -Calculates the offset of the stem from the left edge of the notehead rectangle. Eventually the PDK Framework may be able to provide this instead. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset of stem from the left edge of the notehead rectangle. -]] -function note_entry.calc_stem_offset(entry) - if not entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return left -end - ---[[ -% calc_right_of_all_noteheads - -Calculates the handle offset for an expression with "Right of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_right_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left + right - end - return right -end - ---[[ -% calc_note_at_index - -This function assumes `for note in each(note_entry)` always iterates in the same direction. -(Knowing how the Finale PDK works, it probably iterates from bottom to top note.) -Currently the PDK Framework does not seem to offer a better option. - -@ entry (FCNoteEntry) -@ note_index (number) the zero-based index -]] -function note_entry.calc_note_at_index(entry, note_index) - local x = 0 - for note in each(entry) do - if x == note_index then - return note - end - x = x + 1 - end - return nil -end - ---[[ -% stem_sign - -This is useful for many x,y positioning fields in Finale that mirror +/- -based on stem direction. - -@ entry (FCNoteEntry) -: (number) 1 if upstem, -1 otherwise -]] -function note_entry.stem_sign(entry) - if entry:CalcStemUp() then - return 1 - end - return -1 -end - ---[[ -% duplicate_note - -@ note (FCNote) -: (FCNote | nil) reference to added FCNote or `nil` if not success -]] -function note_entry.duplicate_note(note) - local new_note = note.Entry:AddNewNote() - if nil ~= new_note then - new_note.Displacement = note.Displacement - new_note.RaiseLower = note.RaiseLower - new_note.Tie = note.Tie - new_note.TieBackwards = note.TieBackwards - end - return new_note -end - ---[[ -% delete_note - -Removes the specified FCNote from its associated FCNoteEntry. - -@ note (FCNote) -: (boolean) true if success -]] -function note_entry.delete_note(note) - local entry = note.Entry - if nil == entry then - return false - end - - -- attempt to delete all associated entry-detail mods, but ignore any failures - finale.FCAccidentalMod():EraseAt(note) - finale.FCCrossStaffMod():EraseAt(note) - finale.FCDotMod():EraseAt(note) - finale.FCNoteheadMod():EraseAt(note) - finale.FCPercussionNoteMod():EraseAt(note) - finale.FCTablatureNoteMod():EraseAt(note) - if finale.FCTieMod then -- added in RGP Lua 0.62 - finale.FCTieMod(finale.TIEMODTYPE_TIESTART):EraseAt(note) - finale.FCTieMod(finale.TIEMODTYPE_TIEEND):EraseAt(note) - end - - return entry:DeleteNote(note) -end - ---[[ -% calc_spans_number_of_octaves - -Calculates the numer of octaves spanned by a chord (considering only staff positions, not accidentals). - -@ entry (FCNoteEntry) the entry to calculate from -: (number) of octaves spanned -]] -function note_entry.calc_spans_number_of_octaves(entry) - local top_note = entry:CalcHighestNote(nil) - local bottom_note = entry:CalcLowestNote(nil) - local displacement_diff = top_note.Displacement - bottom_note.Displacement - local num_octaves = math.ceil(displacement_diff / 7) - return num_octaves -end - ---[[ -% add_augmentation_dot - -Adds an augentation dot to the entry. This works even if the entry already has one or more augmentation dots. - -@ entry (FCNoteEntry) the entry to which to add the augmentation dot -]] -function note_entry.add_augmentation_dot(entry) - -- entry.Duration = entry.Duration | (entry.Duration >> 1) -- For Lua 5.3 and higher - entry.Duration = bit32.bor(entry.Duration, bit32.rshift(entry.Duration, 1)) -end - ---[[ -% get_next_same_v - -Returns the next entry in the same V1 or V2 as the input entry. -If the input entry is V2, only the current V2 launch is searched. -If the input entry is V1, only the current measure and layer is searched. - -@ entry (FCNoteEntry) the entry to process -: (FCNoteEntry) the next entry or `nil` in none -]] -function note_entry.get_next_same_v(entry) - local next_entry = entry:Next() - if entry.Voice2 then - if (nil ~= next_entry) and next_entry.Voice2 then - return next_entry - end - return nil - end - if entry.Voice2Launch then - while (nil ~= next_entry) and next_entry.Voice2 do - next_entry = next_entry:Next() - end - end - return next_entry -end - ---[[ -% hide_stem - -Hides the stem of the entry by replacing it with Shape 0. - -@ entry (FCNoteEntry) the entry to process -]] -function note_entry.hide_stem(entry) - local stem = finale.FCCustomStemMod() - stem:SetNoteEntry(entry) - stem:UseUpStemData(entry:CalcStemUp()) - if stem:LoadFirst() then - stem.ShapeID = 0 - stem:Save() - else - stem.ShapeID = 0 - stem:SaveNew() - end -end - - - - -function beam_selected_region() - - local first_in_beam = true - local first_in_beam_v2 = false - local curr_staff = 0 - local curr_layer = -1 - - for entry in eachentrysaved(finenv.Region()) do - if (curr_staff ~= entry:GetStaff()) or (curr_layer ~= entry:GetLayerNumber()) then - first_in_beam = true - first_in_beam_v2 = true - curr_staff = entry:GetStaff() - curr_layer = entry:GetLayerNumber() - end - local isV2 = entry:GetVoice2() - if not isV2 then - first_in_beam_v2 = true - end - if entry:GetDuration() < finale.QUARTER_NOTE then -- less than quarter note duration - if do_unbeam then - entry:SetBeamBeat(true) - elseif (not isV2 and first_in_beam) or (isV2 and first_in_beam_v2) then - entry:SetBeamBeat(true) - if not isV2 then - first_in_beam = false - else - first_in_beam_v2 = false - end - else - entry:SetBeamBeat(false) - end - local next_entry = note_entry.get_next_same_v(entry) - if (nil ~= next_entry) and (next_entry:GetDuration() < finale.QUARTER_NOTE) and not finenv.Region():IsEntryPosWithin(next_entry) then - next_entry:SetBeamBeat(true) - end - end - end -end - -if do_unbeam == nil then - do_unbeam = finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_ALT) or finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_SHIFT) -end -beam_selected_region() + ]]return"Beam Selected Region","Beam Selected Region","Beam Selected Region"end;local o=require("library.note_entry")function beam_selected_region()local p=true;local q=false;local r=0;local s=-1;for t in eachentrysaved(finenv.Region())do if r~=t:GetStaff()or s~=t:GetLayerNumber()then p=true;q=true;r=t:GetStaff()s=t:GetLayerNumber()end;local u=t:GetVoice2()if not u then q=true end;if t:GetDuration()C.EndMeasure or B.EndMeasure0 then M=true;break end;K=K+1 end;if M then local N=finale.FCStaffSystem()N:Load(L:GetFirstSystem())return finale.FCCell(N.FirstMeasure,N.TopStaff)end;local O=finale.FCMusicRegion()O:SetFullDocument()return finale.FCCell(O.EndMeasure,O.EndStaff)end;function w.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local P=finale.FCMusicRegion()P:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),P.StartStaff)end;return w.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function w.get_top_left_selected_or_visible_cell()local I=finenv.Region()if not I:IsEmpty()then return finale.FCCell(I.StartMeasure,I.StartStaff)end;return w.get_top_left_visible_cell()end;function w.is_default_measure_number_visible_on_cell(Q,R,S,T)local U=finale.FCCurrentStaffSpec()if not U:LoadForCell(R,0)then return false end;if Q:GetShowOnTopStaff()and R.Staff==S.TopStaff then return true end;if Q:GetShowOnBottomStaff()and R.Staff==S:CalcBottomStaff()then return true end;if U.ShowMeasureNumbers then return not Q:GetExcludeOtherStaves(T)end;return false end;function w.is_default_number_visible_and_left_aligned(Q,R,V,T,W)if Q.UseScoreInfoForParts then T=false end;if W and Q:GetShowOnMultiMeasureRests(T)then if finale.MNALIGN_LEFT~=Q:GetMultiMeasureAlignment(T)then return false end elseif R.Measure==V.FirstMeasure then if not Q:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=Q:GetStartAlignment(T)then return false end else if not Q:GetShowMultiples(T)then return false end;if finale.MNALIGN_LEFT~=Q:GetMultipleAlignment(T)then return false end end;return w.is_default_measure_number_visible_on_cell(Q,R,V,T)end;function w.update_layout(X,Y)X=X or 1;Y=Y or false;local Z=finale.FCPage()if Z:Load(X)then Z:UpdateLayout(Y)end end;function w.get_current_part()local _=finale.FCParts()_:LoadAll()return _:GetCurrent()end;function w.get_page_format_prefs()local a0=w.get_current_part()local a1=finale.FCPageFormatPrefs()local a2=false;if a0:IsScore()then a2=a1:LoadScore()else a2=a1:LoadParts()end;return a1,a2 end;function w.get_smufl_metadata_file(a3)if not a3 then a3=finale.FCFontInfo()a3:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local a4=function(a5,a3)local a6=a5 .."/SMuFL/Fonts/"..a3.Name.."/"..a3.Name..".json"return io.open(a6,"r")end;local a7=""if finenv.UI():IsOnWindows()then a7=os.getenv("LOCALAPPDATA")else a7=os.getenv("HOME").."/Library/Application Support"end;local a8=a4(a7,a3)if nil~=a8 then return a8 end;local a9="/Library/Application Support"if finenv.UI():IsOnWindows()then a9=os.getenv("COMMONPROGRAMFILES")end;return a4(a9,a3)end;function w.is_font_smufl_font(a3)if not a3 then a3=finale.FCFontInfo()a3:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=w.finale_version(27,1)then if nil~=a3.IsSMuFLFont then return a3.IsSMuFLFont end end;local aa=w.get_smufl_metadata_file(a3)if nil~=aa then io.close(aa)return true end;return false end;function w.simple_input(ab,ac)local ad=finale.FCString()ad.LuaString=""local ae=finale.FCString()local af=160;function format_ctrl(ag,ah,ai,aj)ag:SetHeight(ah)ag:SetWidth(ai)ae.LuaString=aj;ag:SetText(ae)end;title_width=string.len(ab)*6+54;if title_width>af then af=title_width end;text_width=string.len(ac)*6;if text_width>af then af=text_width end;ae.LuaString=ab;local ak=finale.FCCustomLuaWindow()ak:SetTitle(ae)local al=ak:CreateStatic(0,0)format_ctrl(al,16,af,ac)local am=ak:CreateEdit(0,20)format_ctrl(am,20,af,"")ak:CreateOkButton()ak:CreateCancelButton()function callback(ag)end;ak:RegisterHandleCommand(callback)if ak:ExecuteModal(nil)==finale.EXECMODAL_OK then ad.LuaString=am:GetText(ad)return ad.LuaString end end;function w.is_finale_object(an)return an and type(an)=="userdata"and an.ClassName and an.GetClassID and true or false end;function w.system_indent_set_to_prefs(V,a1)a1=a1 or w.get_page_format_prefs()local ao=finale.FCMeasure()local ap=V.FirstMeasure==1;if not ap and ao:Load(V.FirstMeasure)then if ao.ShowFullNames then ap=true end end;if ap and a1.UseFirstSystemMargins then V.LeftMargin=a1.FirstSystemLeft else V.LeftMargin=a1.SystemLeft end;return V:Save()end;return w end)return a("__root") \ No newline at end of file diff --git a/dist/beam_unbeam_selected_region.lua b/dist/beam_unbeam_selected_region.lua deleted file mode 100644 index 779bd071..00000000 --- a/dist/beam_unbeam_selected_region.lua +++ /dev/null @@ -1,429 +0,0 @@ -function plugindef() - -- This function and the 'finaleplugin' namespace - -- are both reserved for the plug-in definition. - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0.1" - finaleplugin.Date = "June 12, 2020" - finaleplugin.CategoryTags = "Note" - return "Unbeam Selected Region", "Unbeam Selected Region", "Unbeam Selected Region" -end - ---[[ -$module Note Entry -]] -- -local note_entry = {} - ---[[ -% get_music_region - -Returns an intance of `FCMusicRegion` that corresponds to the metric location of the input note entry. - -@ entry (FCNoteEntry) -: (FCMusicRegion) -]] -function note_entry.get_music_region(entry) - local exp_region = finale.FCMusicRegion() - exp_region:SetCurrentSelection() -- called to match the selected IU list (e.g., if using Staff Sets) - exp_region.StartStaff = entry.Staff - exp_region.EndStaff = entry.Staff - exp_region.StartMeasure = entry.Measure - exp_region.EndMeasure = entry.Measure - exp_region.StartMeasurePos = entry.MeasurePos - exp_region.EndMeasurePos = entry.MeasurePos - return exp_region -end - --- entry_metrics can be omitted, in which case they are constructed and released here --- return entry_metrics, loaded_here -local use_or_get_passed_in_entry_metrics = function(entry, entry_metrics) - if entry_metrics then - return entry_metrics, false - end - entry_metrics = finale.FCEntryMetrics() - if entry_metrics:Load(entry) then - return entry_metrics, true - end - return nil, false -end - ---[[ -% get_evpu_notehead_height - -Returns the calculated height of the notehead rectangle. - -@ entry (FCNoteEntry) - -: (number) the EVPU height -]] -function note_entry.get_evpu_notehead_height(entry) - local highest_note = entry:CalcHighestNote(nil) - local lowest_note = entry:CalcLowestNote(nil) - local evpu_height = (2 + highest_note:CalcStaffPosition() - lowest_note:CalcStaffPosition()) * 12 -- 12 evpu per staff step; add 2 staff steps to accommodate for notehead height at top and bottom - return evpu_height -end - ---[[ -% get_top_note_position - -Returns the vertical page coordinate of the top of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_top_note_position(entry, entry_metrics) - local retval = -math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if not entry:CalcStemUp() then - retval = entry_metrics.TopPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.BottomPosition + scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% get_bottom_note_position - -Returns the vertical page coordinate of the bottom of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_bottom_note_position(entry, entry_metrics) - local retval = math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if entry:CalcStemUp() then - retval = entry_metrics.BottomPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.TopPosition - scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% calc_widths - -Get the widest left-side notehead width and widest right-side notehead width. - -@ entry (FCNoteEntry) -: (number, number) widest left-side notehead width and widest right-side notehead width -]] -function note_entry.calc_widths(entry) - local left_width = 0 - local right_width = 0 - for note in each(entry) do - local note_width = note:CalcNoteheadWidth() - if note_width > 0 then - if note:CalcRightsidePlacement() then - if note_width > right_width then - right_width = note_width - end - else - if note_width > left_width then - left_width = note_width - end - end - end - end - return left_width, right_width -end - --- These functions return the offset for an expression handle. --- Expression handles are vertical when they are left-aligned --- with the primary notehead rectangle. - ---[[ -% calc_left_of_all_noteheads - -Calculates the handle offset for an expression with "Left of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_all_noteheads(entry) - if entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return -left -end - ---[[ -% calc_left_of_primary_notehead - -Calculates the handle offset for an expression with "Left of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_primary_notehead(entry) - return 0 -end - ---[[ -% calc_center_of_all_noteheads - -Calculates the handle offset for an expression with "Center of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - local width_centered = (left + right) / 2 - if not entry:CalcStemUp() then - width_centered = width_centered - left - end - return width_centered -end - ---[[ -% calc_center_of_primary_notehead - -Calculates the handle offset for an expression with "Center of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_primary_notehead(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left / 2 - end - return right / 2 -end - ---[[ -% calc_stem_offset - -Calculates the offset of the stem from the left edge of the notehead rectangle. Eventually the PDK Framework may be able to provide this instead. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset of stem from the left edge of the notehead rectangle. -]] -function note_entry.calc_stem_offset(entry) - if not entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return left -end - ---[[ -% calc_right_of_all_noteheads - -Calculates the handle offset for an expression with "Right of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_right_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left + right - end - return right -end - ---[[ -% calc_note_at_index - -This function assumes `for note in each(note_entry)` always iterates in the same direction. -(Knowing how the Finale PDK works, it probably iterates from bottom to top note.) -Currently the PDK Framework does not seem to offer a better option. - -@ entry (FCNoteEntry) -@ note_index (number) the zero-based index -]] -function note_entry.calc_note_at_index(entry, note_index) - local x = 0 - for note in each(entry) do - if x == note_index then - return note - end - x = x + 1 - end - return nil -end - ---[[ -% stem_sign - -This is useful for many x,y positioning fields in Finale that mirror +/- -based on stem direction. - -@ entry (FCNoteEntry) -: (number) 1 if upstem, -1 otherwise -]] -function note_entry.stem_sign(entry) - if entry:CalcStemUp() then - return 1 - end - return -1 -end - ---[[ -% duplicate_note - -@ note (FCNote) -: (FCNote | nil) reference to added FCNote or `nil` if not success -]] -function note_entry.duplicate_note(note) - local new_note = note.Entry:AddNewNote() - if nil ~= new_note then - new_note.Displacement = note.Displacement - new_note.RaiseLower = note.RaiseLower - new_note.Tie = note.Tie - new_note.TieBackwards = note.TieBackwards - end - return new_note -end - ---[[ -% delete_note - -Removes the specified FCNote from its associated FCNoteEntry. - -@ note (FCNote) -: (boolean) true if success -]] -function note_entry.delete_note(note) - local entry = note.Entry - if nil == entry then - return false - end - - -- attempt to delete all associated entry-detail mods, but ignore any failures - finale.FCAccidentalMod():EraseAt(note) - finale.FCCrossStaffMod():EraseAt(note) - finale.FCDotMod():EraseAt(note) - finale.FCNoteheadMod():EraseAt(note) - finale.FCPercussionNoteMod():EraseAt(note) - finale.FCTablatureNoteMod():EraseAt(note) - -- finale.FCTieMod():EraseAt(note) -- FCTieMod is not currently lua supported, but leave this here in case it ever is - - return entry:DeleteNote(note) -end - ---[[ -% calc_spans_number_of_octaves - -Calculates the numer of octaves spanned by a chord (considering only staff positions, not accidentals). - -@ entry (FCNoteEntry) the entry to calculate from -: (number) of octaves spanned -]] -function note_entry.calc_spans_number_of_octaves(entry) - local top_note = entry:CalcHighestNote(nil) - local bottom_note = entry:CalcLowestNote(nil) - local displacement_diff = top_note.Displacement - bottom_note.Displacement - local num_octaves = math.ceil(displacement_diff / 7) - return num_octaves -end - ---[[ -% add_augmentation_dot - -Adds an augentation dot to the entry. This works even if the entry already has one or more augmentation dots. - -@ entry (FCNoteEntry) the entry to which to add the augmentation dot -]] -function note_entry.add_augmentation_dot(entry) - -- entry.Duration = entry.Duration | (entry.Duration >> 1) -- For Lua 5.3 and higher - entry.Duration = bit32.bor(entry.Duration, bit32.rshift(entry.Duration, 1)) -end - ---[[ -% get_next_same_v - -Returns the next entry in the same V1 or V2 as the input entry. -If the input entry is V2, only the current V2 launch is searched. -If the input entry is V1, only the current measure and layer is searched. - -@ entry (FCNoteEntry) the entry to process -: (FCNoteEntry) the next entry or `nil` in none -]] -function note_entry.get_next_same_v(entry) - local next_entry = entry:Next() - if entry.Voice2 then - if (nil ~= next_entry) and next_entry.Voice2 then - return next_entry - end - return nil - end - if entry.Voice2Launch then - while (nil ~= next_entry) and next_entry.Voice2 do - next_entry = next_entry:Next() - end - end - return next_entry -end - ---[[ -% hide_stem - -Hides the stem of the entry by replacing it with Shape 0. - -@ entry (FCNoteEntry) the entry to process -]] -function note_entry.hide_stem(entry) - local stem = finale.FCCustomStemMod() - stem:SetNoteEntry(entry) - stem:UseUpStemData(entry:CalcStemUp()) - if stem:LoadFirst() then - stem.ShapeID = 0 - stem:Save() - else - stem.ShapeID = 0 - stem:SaveNew() - end -end - - - - -function unbeam_selected_region() - for entry in eachentrysaved(finenv.Region()) do - local isV2 = entry:GetVoice2() - if entry:GetDuration() < 1024 then -- less than quarter note duration - entry:SetBeamBeat(true) - end - local next_entry = note_entry.get_next_same_v(entry) - if (nil ~= next_entry) and (next_entry:GetDuration() < 1024) and - not finenv.Region():IsEntryPosWithin(next_entry) then - next_entry:SetBeamBeat(true) - end - end -end - -unbeam_selected_region() diff --git a/dist/chord_accidental_adjust_down.lua b/dist/chord_accidental_adjust_down.lua index 00206ee6..e2ee7592 100644 --- a/dist/chord_accidental_adjust_down.lua +++ b/dist/chord_accidental_adjust_down.lua @@ -1,312 +1 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Michael McClennan" - finaleplugin.Version = "1.0" - finaleplugin.Date = "August 14, 2021" - finaleplugin.AuthorURL = "www.michaelmcclennan.com" - finaleplugin.AuthorEmail = "info@michaelmcclennan.com" - finaleplugin.CategoryTags = "Chord" - return "Chord Accidental - Move Down", "Adjust Chord Accidental Down", "Adjust the accidental of chord symbol down" -end - - --- Author: Robert Patterson --- Date: March 5, 2021 ---[[ -$module Configuration - -This library implements a UTF-8 text file scheme for configuration and user settings as follows: - -- Comments start with `--` -- Leading, trailing, and extra whitespace is ignored -- Each parameter is named and delimited as follows: - -``` - = -``` - -Parameter values may be: - -- Strings delimited with either single- or double-quotes -- Tables delimited with `{}` that may contain strings, booleans, or numbers -- Booleans (`true` or `false`) -- Numbers - -Currently the following are not supported: - -- Tables embedded within tables -- Tables containing strings that contain commas - -A sample configuration file might be: - -```lua --- Configuration File for "Hairpin and Dynamic Adjustments" script --- -left_dynamic_cushion = 12 --evpus -right_dynamic_cushion = -6 --evpus -``` - -## Configuration Files - -Configuration files provide a way for power users to modify script behavior without -having to modify the script itself. Some users track their changes to their configuration files, -so scripts should not create or modify them programmatically. - -- The user creates each configuration file in a subfolder called `script_settings` within -the folder of the calling script. -- Each script that has a configuration file defines its own configuration file name. -- It is entirely appropriate over time for scripts to transition from configuration files to user settings, -but this requires implementing a user interface to modify the user settings from within the script. -(See below.) - -## User Settings Files - -User settings are written by the scripts themselves and reside in the user's preferences folder -in an appropriately-named location for the operating system. (The naming convention is a detail that the -configuration library handles for the caller.) If the user settings are to be changed from their defaults, -the script itself should provide a means to change them. This could be a (preferably optional) dialog box -or any other mechanism the script author chooses. - -User settings are saved in the user's preferences folder (on Mac) or AppData folder (on Windows). - -## Merge Process - -Files are _merged_ into the passed-in list of default values. They do not _replace_ the list. Each calling script contains -a table of all the configurable parameters or settings it recognizes along with default values. An example: - -`sample.lua:` - -```lua -parameters = { - x = 1, - y = 2, - z = 3 -} - -configuration.get_parameters(parameters, "script.config.txt") - -for k, v in pairs(parameters) do - print(k, v) -end -``` - -Suppose the `script.config.text` file is as follows: - -``` -y = 4 -q = 6 -``` - -The returned parameters list is: - - -```lua -parameters = { - x = 1, -- remains the default value passed in - y = 4, -- replaced value from the config file - z = 3 -- remains the default value passed in -} -``` - -The `q` parameter in the config file is ignored because the input paramater list -had no `q` parameter. - -This approach allows total flexibility for the script add to or modify its list of parameters -without having to worry about older configuration files or user settings affecting it. -]] - -local configuration = {} - -local script_settings_dir = "script_settings" -- the parent of this directory is the running lua path -local comment_marker = "--" -local parameter_delimiter = "=" -local path_delimiter = "/" - -local file_exists = function(file_path) - local f = io.open(file_path, "r") - if nil ~= f then - io.close(f) - return true - end - return false -end - -local strip_leading_trailing_whitespace = function(str) - return str:match("^%s*(.-)%s*$") -- lua pattern magic taken from the Internet -end - -local parse_table = function(val_string) - local ret_table = {} - for element in val_string:gmatch("[^,%s]+") do -- lua pattern magic taken from the Internet - local parsed_element = parse_parameter(element) - table.insert(ret_table, parsed_element) - end - return ret_table -end - -parse_parameter = function(val_string) - if "\"" == val_string:sub(1, 1) and "\"" == val_string:sub(#val_string, #val_string) then -- double-quote string - return string.gsub(val_string, "\"(.+)\"", "%1") -- lua pattern magic: "(.+)" matches all characters between two double-quote marks (no escape chars) - elseif "'" == val_string:sub(1, 1) and "'" == val_string:sub(#val_string, #val_string) then -- single-quote string - return string.gsub(val_string, "'(.+)'", "%1") -- lua pattern magic: '(.+)' matches all characters between two single-quote marks (no escape chars) - elseif "{" == val_string:sub(1, 1) and "}" == val_string:sub(#val_string, #val_string) then - return parse_table(string.gsub(val_string, "{(.+)}", "%1")) - elseif "true" == val_string then - return true - elseif "false" == val_string then - return false - end - return tonumber(val_string) -end - -local get_parameters_from_file = function(file_path, parameter_list) - local file_parameters = {} - - if not file_exists(file_path) then - return false - end - - for line in io.lines(file_path) do - local comment_at = string.find(line, comment_marker, 1, true) -- true means find raw string rather than lua pattern - if nil ~= comment_at then - line = string.sub(line, 1, comment_at - 1) - end - local delimiter_at = string.find(line, parameter_delimiter, 1, true) - if nil ~= delimiter_at then - local name = strip_leading_trailing_whitespace(string.sub(line, 1, delimiter_at - 1)) - local val_string = strip_leading_trailing_whitespace(string.sub(line, delimiter_at + 1)) - file_parameters[name] = parse_parameter(val_string) - end - end - - for param_name, _ in pairs(parameter_list) do - local param_val = file_parameters[param_name] - if nil ~= param_val then - parameter_list[param_name] = param_val - end - end - - return true -end - ---[[ -% get_parameters - -Searches for a file with the input filename in the `script_settings` directory and replaces the default values in `parameter_list` -with any that are found in the config file. - -@ file_name (string) the file name of the config file (which will be prepended with the `script_settings` directory) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true if the file exists -]] -function configuration.get_parameters(file_name, parameter_list) - local path = "" - if finenv.IsRGPLua then - path = finenv.RunningLuaFolderPath() - else - local str = finale.FCString() - str:SetRunningLuaFolderPath() - path = str.LuaString - end - local file_path = path .. script_settings_dir .. path_delimiter .. file_name - return get_parameters_from_file(file_path, parameter_list) -end - --- Calculates a filepath in the user's preferences folder using recommended naming conventions --- -local calc_preferences_filepath = function(script_name) - local str = finale.FCString() - str:SetUserOptionsPath() - local folder_name = str.LuaString - if not finenv.IsRGPLua and finenv.UI():IsOnMac() then - -- works around bug in SetUserOptionsPath() in JW Lua - folder_name = os.getenv("HOME") .. folder_name:sub(2) -- strip '~' and replace with actual folder - end - if finenv.UI():IsOnWindows() then - folder_name = folder_name .. path_delimiter .. "FinaleLua" - end - local file_path = folder_name .. path_delimiter - if finenv.UI():IsOnMac() then - file_path = file_path .. "com.finalelua." - end - file_path = file_path .. script_name .. ".settings.txt" - return file_path, folder_name -end - ---[[ -% save_user_settings - -Saves the user's preferences for a script from the values provided in `parameter_list`. - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true on success -]] -function configuration.save_user_settings(script_name, parameter_list) - local file_path, folder_path = calc_preferences_filepath(script_name) - local file = io.open(file_path, "w") - if not file and finenv.UI():IsOnWindows() then -- file not found - os.execute('mkdir "' .. folder_path ..'"') -- so try to make a folder (windows only, since the folder is guaranteed to exist on mac) - file = io.open(file_path, "w") -- try the file again - end - if not file then -- still couldn't find file - return false -- so give up - end - file:write("-- User settings for " .. script_name .. ".lua\n\n") - for k,v in pairs(parameter_list) do -- only number, boolean, or string values - if type(v) == "string" then - v = "\"" .. v .."\"" - else - v = tostring(v) - end - file:write(k, " = ", v, "\n") - end - file:close() - return true -- success -end - ---[[ -% get_user_settings - -Find the user's settings for a script in the preferences directory and replaces the default values in `parameter_list` -with any that are found in the preferences file. The actual name and path of the preferences file is OS dependent, so -the input string should just be the script name (without an extension). - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -@ [create_automatically] (boolean) if true, create the file automatically (default is `true`) -: (boolean) `true` if the file already existed, `false` if it did not or if it was created automatically -]] -function configuration.get_user_settings(script_name, parameter_list, create_automatically) - if create_automatically == nil then create_automatically = true end - local exists = get_parameters_from_file(calc_preferences_filepath(script_name), parameter_list) - if not exists and create_automatically then - configuration.save_user_settings(script_name, parameter_list) - end - return exists -end - - - -local config = {vertical_increment = 5} - -configuration.get_parameters("chord_accidental_adjust.config.txt", config) - -function chord_accidental_adjust_down() - local chordprefs = finale.FCChordPrefs() - chordprefs:Load(1) - local my_distance_result_flat = chordprefs:GetFlatBaselineAdjustment() - local my_distance_result_sharp = chordprefs:GetSharpBaselineAdjustment() - local my_distance_result_natural = chordprefs:GetNaturalBaselineAdjustment() - local chordprefs = finale.FCChordPrefs() - chordprefs:Load(1) - chordprefs:GetFlatBaselineAdjustment() - chordprefs.FlatBaselineAdjustment = -1 * config.vertical_increment + my_distance_result_flat - chordprefs:GetSharpBaselineAdjustment() - chordprefs.SharpBaselineAdjustment = -1 * config.vertical_increment + my_distance_result_sharp - chordprefs:GetNaturalBaselineAdjustment() - chordprefs.NaturalBaselineAdjustment = -1 * config.vertical_increment + my_distance_result_natural - chordprefs:Save() -end - -chord_accidental_adjust_down() +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Michael McClennan"finaleplugin.Version="1.0"finaleplugin.Date="August 14, 2021"finaleplugin.AuthorURL="www.michaelmcclennan.com"finaleplugin.AuthorEmail="info@michaelmcclennan.com"finaleplugin.CategoryTags="Chord"return"Chord Accidental - Move Down","Adjust Chord Accidental Down","Adjust the accidental of chord symbol down"end;local o=require("library.configuration")local p={vertical_increment=5}o.get_parameters("chord_accidental_adjust.config.txt",p)function chord_accidental_adjust_down()local q=finale.FCChordPrefs()q:Load(1)local r=q:GetFlatBaselineAdjustment()local s=q:GetSharpBaselineAdjustment()local t=q:GetNaturalBaselineAdjustment()local q=finale.FCChordPrefs()q:Load(1)q:GetFlatBaselineAdjustment()q.FlatBaselineAdjustment=-1*p.vertical_increment+r;q:GetSharpBaselineAdjustment()q.SharpBaselineAdjustment=-1*p.vertical_increment+s;q:GetNaturalBaselineAdjustment()q.NaturalBaselineAdjustment=-1*p.vertical_increment+t;q:Save()end;chord_accidental_adjust_down()end)c("library.configuration",function(require,n,c,d)local u={}function u.finale_version(v,w,x)local y=bit32.bor(bit32.lshift(math.floor(v),24),bit32.lshift(math.floor(w),20))if x then y=bit32.bor(y,math.floor(x))end;return y end;function u.group_overlaps_region(z,A)if A:IsFullDocumentSpan()then return true end;local B=false;local C=finale.FCSystemStaves()C:LoadAllForRegion(A)for D in each(C)do if z:ContainsStaff(D:GetStaff())then B=true;break end end;if not B then return false end;if z.StartMeasure>A.EndMeasure or z.EndMeasure0 then K=true;break end;I=I+1 end;if K then local L=finale.FCStaffSystem()L:Load(J:GetFirstSystem())return finale.FCCell(L.FirstMeasure,L.TopStaff)end;local M=finale.FCMusicRegion()M:SetFullDocument()return finale.FCCell(M.EndMeasure,M.EndStaff)end;function u.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local N=finale.FCMusicRegion()N:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),N.StartStaff)end;return u.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function u.get_top_left_selected_or_visible_cell()local G=finenv.Region()if not G:IsEmpty()then return finale.FCCell(G.StartMeasure,G.StartStaff)end;return u.get_top_left_visible_cell()end;function u.is_default_measure_number_visible_on_cell(O,P,Q,R)local S=finale.FCCurrentStaffSpec()if not S:LoadForCell(P,0)then return false end;if O:GetShowOnTopStaff()and P.Staff==Q.TopStaff then return true end;if O:GetShowOnBottomStaff()and P.Staff==Q:CalcBottomStaff()then return true end;if S.ShowMeasureNumbers then return not O:GetExcludeOtherStaves(R)end;return false end;function u.is_default_number_visible_and_left_aligned(O,P,T,R,U)if O.UseScoreInfoForParts then R=false end;if U and O:GetShowOnMultiMeasureRests(R)then if finale.MNALIGN_LEFT~=O:GetMultiMeasureAlignment(R)then return false end elseif P.Measure==T.FirstMeasure then if not O:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=O:GetStartAlignment(R)then return false end else if not O:GetShowMultiples(R)then return false end;if finale.MNALIGN_LEFT~=O:GetMultipleAlignment(R)then return false end end;return u.is_default_measure_number_visible_on_cell(O,P,T,R)end;function u.update_layout(V,W)V=V or 1;W=W or false;local X=finale.FCPage()if X:Load(V)then X:UpdateLayout(W)end end;function u.get_current_part()local Y=finale.FCParts()Y:LoadAll()return Y:GetCurrent()end;function u.get_page_format_prefs()local Z=u.get_current_part()local _=finale.FCPageFormatPrefs()local a0=false;if Z:IsScore()then a0=_:LoadScore()else a0=_:LoadParts()end;return _,a0 end;function u.get_smufl_metadata_file(a1)if not a1 then a1=finale.FCFontInfo()a1:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local a2=function(a3,a1)local a4=a3 .."/SMuFL/Fonts/"..a1.Name.."/"..a1.Name..".json"return io.open(a4,"r")end;local a5=""if finenv.UI():IsOnWindows()then a5=os.getenv("LOCALAPPDATA")else a5=os.getenv("HOME").."/Library/Application Support"end;local a6=a2(a5,a1)if nil~=a6 then return a6 end;local a7="/Library/Application Support"if finenv.UI():IsOnWindows()then a7=os.getenv("COMMONPROGRAMFILES")end;return a2(a7,a1)end;function u.is_font_smufl_font(a1)if not a1 then a1=finale.FCFontInfo()a1:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=u.finale_version(27,1)then if nil~=a1.IsSMuFLFont then return a1.IsSMuFLFont end end;local a8=u.get_smufl_metadata_file(a1)if nil~=a8 then io.close(a8)return true end;return false end;function u.simple_input(a9,aa)local ab=finale.FCString()ab.LuaString=""local ac=finale.FCString()local ad=160;function format_ctrl(ae,af,ag,ah)ae:SetHeight(af)ae:SetWidth(ag)ac.LuaString=ah;ae:SetText(ac)end;title_width=string.len(a9)*6+54;if title_width>ad then ad=title_width end;text_width=string.len(aa)*6;if text_width>ad then ad=text_width end;ac.LuaString=a9;local ai=finale.FCCustomLuaWindow()ai:SetTitle(ac)local aj=ai:CreateStatic(0,0)format_ctrl(aj,16,ad,aa)local ak=ai:CreateEdit(0,20)format_ctrl(ak,20,ad,"")ai:CreateOkButton()ai:CreateCancelButton()function callback(ae)end;ai:RegisterHandleCommand(callback)if ai:ExecuteModal(nil)==finale.EXECMODAL_OK then ab.LuaString=ak:GetText(ab)return ab.LuaString end end;function u.is_finale_object(al)return al and type(al)=="userdata"and al.ClassName and al.GetClassID and true or false end;function u.system_indent_set_to_prefs(T,_)_=_ or u.get_page_format_prefs()local am=finale.FCMeasure()local an=T.FirstMeasure==1;if not an and am:Load(T.FirstMeasure)then if am.ShowFullNames then an=true end end;if an and _.UseFirstSystemMargins then T.LeftMargin=_.FirstSystemLeft else T.LeftMargin=_.SystemLeft end;return T:Save()end;return u end)return a("__root") \ No newline at end of file diff --git a/dist/chord_accidental_adjust_up.lua b/dist/chord_accidental_adjust_up.lua index 783612f7..13e0c337 100644 --- a/dist/chord_accidental_adjust_up.lua +++ b/dist/chord_accidental_adjust_up.lua @@ -1,312 +1 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Michael McClennan" - finaleplugin.Version = "1.0" - finaleplugin.Date = "August 14, 2021" - finaleplugin.AuthorURL = "www.michaelmcclennan.com" - finaleplugin.AuthorEmail = "info@michaelmcclennan.com" - finaleplugin.CategoryTags = "Chord" - return "Chord Accidental - Move Up", "Adjust Chord Accidental Up", "Adjust the accidental of chord symbol up" -end - - --- Author: Robert Patterson --- Date: March 5, 2021 ---[[ -$module Configuration - -This library implements a UTF-8 text file scheme for configuration and user settings as follows: - -- Comments start with `--` -- Leading, trailing, and extra whitespace is ignored -- Each parameter is named and delimited as follows: - -``` - = -``` - -Parameter values may be: - -- Strings delimited with either single- or double-quotes -- Tables delimited with `{}` that may contain strings, booleans, or numbers -- Booleans (`true` or `false`) -- Numbers - -Currently the following are not supported: - -- Tables embedded within tables -- Tables containing strings that contain commas - -A sample configuration file might be: - -```lua --- Configuration File for "Hairpin and Dynamic Adjustments" script --- -left_dynamic_cushion = 12 --evpus -right_dynamic_cushion = -6 --evpus -``` - -## Configuration Files - -Configuration files provide a way for power users to modify script behavior without -having to modify the script itself. Some users track their changes to their configuration files, -so scripts should not create or modify them programmatically. - -- The user creates each configuration file in a subfolder called `script_settings` within -the folder of the calling script. -- Each script that has a configuration file defines its own configuration file name. -- It is entirely appropriate over time for scripts to transition from configuration files to user settings, -but this requires implementing a user interface to modify the user settings from within the script. -(See below.) - -## User Settings Files - -User settings are written by the scripts themselves and reside in the user's preferences folder -in an appropriately-named location for the operating system. (The naming convention is a detail that the -configuration library handles for the caller.) If the user settings are to be changed from their defaults, -the script itself should provide a means to change them. This could be a (preferably optional) dialog box -or any other mechanism the script author chooses. - -User settings are saved in the user's preferences folder (on Mac) or AppData folder (on Windows). - -## Merge Process - -Files are _merged_ into the passed-in list of default values. They do not _replace_ the list. Each calling script contains -a table of all the configurable parameters or settings it recognizes along with default values. An example: - -`sample.lua:` - -```lua -parameters = { - x = 1, - y = 2, - z = 3 -} - -configuration.get_parameters(parameters, "script.config.txt") - -for k, v in pairs(parameters) do - print(k, v) -end -``` - -Suppose the `script.config.text` file is as follows: - -``` -y = 4 -q = 6 -``` - -The returned parameters list is: - - -```lua -parameters = { - x = 1, -- remains the default value passed in - y = 4, -- replaced value from the config file - z = 3 -- remains the default value passed in -} -``` - -The `q` parameter in the config file is ignored because the input paramater list -had no `q` parameter. - -This approach allows total flexibility for the script add to or modify its list of parameters -without having to worry about older configuration files or user settings affecting it. -]] - -local configuration = {} - -local script_settings_dir = "script_settings" -- the parent of this directory is the running lua path -local comment_marker = "--" -local parameter_delimiter = "=" -local path_delimiter = "/" - -local file_exists = function(file_path) - local f = io.open(file_path, "r") - if nil ~= f then - io.close(f) - return true - end - return false -end - -local strip_leading_trailing_whitespace = function(str) - return str:match("^%s*(.-)%s*$") -- lua pattern magic taken from the Internet -end - -local parse_table = function(val_string) - local ret_table = {} - for element in val_string:gmatch("[^,%s]+") do -- lua pattern magic taken from the Internet - local parsed_element = parse_parameter(element) - table.insert(ret_table, parsed_element) - end - return ret_table -end - -parse_parameter = function(val_string) - if "\"" == val_string:sub(1, 1) and "\"" == val_string:sub(#val_string, #val_string) then -- double-quote string - return string.gsub(val_string, "\"(.+)\"", "%1") -- lua pattern magic: "(.+)" matches all characters between two double-quote marks (no escape chars) - elseif "'" == val_string:sub(1, 1) and "'" == val_string:sub(#val_string, #val_string) then -- single-quote string - return string.gsub(val_string, "'(.+)'", "%1") -- lua pattern magic: '(.+)' matches all characters between two single-quote marks (no escape chars) - elseif "{" == val_string:sub(1, 1) and "}" == val_string:sub(#val_string, #val_string) then - return parse_table(string.gsub(val_string, "{(.+)}", "%1")) - elseif "true" == val_string then - return true - elseif "false" == val_string then - return false - end - return tonumber(val_string) -end - -local get_parameters_from_file = function(file_path, parameter_list) - local file_parameters = {} - - if not file_exists(file_path) then - return false - end - - for line in io.lines(file_path) do - local comment_at = string.find(line, comment_marker, 1, true) -- true means find raw string rather than lua pattern - if nil ~= comment_at then - line = string.sub(line, 1, comment_at - 1) - end - local delimiter_at = string.find(line, parameter_delimiter, 1, true) - if nil ~= delimiter_at then - local name = strip_leading_trailing_whitespace(string.sub(line, 1, delimiter_at - 1)) - local val_string = strip_leading_trailing_whitespace(string.sub(line, delimiter_at + 1)) - file_parameters[name] = parse_parameter(val_string) - end - end - - for param_name, _ in pairs(parameter_list) do - local param_val = file_parameters[param_name] - if nil ~= param_val then - parameter_list[param_name] = param_val - end - end - - return true -end - ---[[ -% get_parameters - -Searches for a file with the input filename in the `script_settings` directory and replaces the default values in `parameter_list` -with any that are found in the config file. - -@ file_name (string) the file name of the config file (which will be prepended with the `script_settings` directory) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true if the file exists -]] -function configuration.get_parameters(file_name, parameter_list) - local path = "" - if finenv.IsRGPLua then - path = finenv.RunningLuaFolderPath() - else - local str = finale.FCString() - str:SetRunningLuaFolderPath() - path = str.LuaString - end - local file_path = path .. script_settings_dir .. path_delimiter .. file_name - return get_parameters_from_file(file_path, parameter_list) -end - --- Calculates a filepath in the user's preferences folder using recommended naming conventions --- -local calc_preferences_filepath = function(script_name) - local str = finale.FCString() - str:SetUserOptionsPath() - local folder_name = str.LuaString - if not finenv.IsRGPLua and finenv.UI():IsOnMac() then - -- works around bug in SetUserOptionsPath() in JW Lua - folder_name = os.getenv("HOME") .. folder_name:sub(2) -- strip '~' and replace with actual folder - end - if finenv.UI():IsOnWindows() then - folder_name = folder_name .. path_delimiter .. "FinaleLua" - end - local file_path = folder_name .. path_delimiter - if finenv.UI():IsOnMac() then - file_path = file_path .. "com.finalelua." - end - file_path = file_path .. script_name .. ".settings.txt" - return file_path, folder_name -end - ---[[ -% save_user_settings - -Saves the user's preferences for a script from the values provided in `parameter_list`. - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true on success -]] -function configuration.save_user_settings(script_name, parameter_list) - local file_path, folder_path = calc_preferences_filepath(script_name) - local file = io.open(file_path, "w") - if not file and finenv.UI():IsOnWindows() then -- file not found - os.execute('mkdir "' .. folder_path ..'"') -- so try to make a folder (windows only, since the folder is guaranteed to exist on mac) - file = io.open(file_path, "w") -- try the file again - end - if not file then -- still couldn't find file - return false -- so give up - end - file:write("-- User settings for " .. script_name .. ".lua\n\n") - for k,v in pairs(parameter_list) do -- only number, boolean, or string values - if type(v) == "string" then - v = "\"" .. v .."\"" - else - v = tostring(v) - end - file:write(k, " = ", v, "\n") - end - file:close() - return true -- success -end - ---[[ -% get_user_settings - -Find the user's settings for a script in the preferences directory and replaces the default values in `parameter_list` -with any that are found in the preferences file. The actual name and path of the preferences file is OS dependent, so -the input string should just be the script name (without an extension). - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -@ [create_automatically] (boolean) if true, create the file automatically (default is `true`) -: (boolean) `true` if the file already existed, `false` if it did not or if it was created automatically -]] -function configuration.get_user_settings(script_name, parameter_list, create_automatically) - if create_automatically == nil then create_automatically = true end - local exists = get_parameters_from_file(calc_preferences_filepath(script_name), parameter_list) - if not exists and create_automatically then - configuration.save_user_settings(script_name, parameter_list) - end - return exists -end - - - -local config = {vertical_increment = 5} - -configuration.get_parameters("chord_accidental_adjust.config.txt", config) - -function chord_accidental_adjust_up() - local chordprefs = finale.FCChordPrefs() - chordprefs:Load(1) - local my_distance_result_flat = chordprefs:GetFlatBaselineAdjustment() - local my_distance_result_sharp = chordprefs:GetSharpBaselineAdjustment() - local my_distance_result_natural = chordprefs:GetNaturalBaselineAdjustment() - local chordprefs = finale.FCChordPrefs() - chordprefs:Load(1) - chordprefs:GetFlatBaselineAdjustment() - chordprefs.FlatBaselineAdjustment = config.vertical_increment + my_distance_result_flat - chordprefs:GetSharpBaselineAdjustment() - chordprefs.SharpBaselineAdjustment = config.vertical_increment + my_distance_result_sharp - chordprefs:GetNaturalBaselineAdjustment() - chordprefs.NaturalBaselineAdjustment = config.vertical_increment + my_distance_result_natural - chordprefs:Save() -end - -chord_accidental_adjust_up() +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Michael McClennan"finaleplugin.Version="1.0"finaleplugin.Date="August 14, 2021"finaleplugin.AuthorURL="www.michaelmcclennan.com"finaleplugin.AuthorEmail="info@michaelmcclennan.com"finaleplugin.CategoryTags="Chord"return"Chord Accidental - Move Up","Adjust Chord Accidental Up","Adjust the accidental of chord symbol up"end;local o=require("library.configuration")local p={vertical_increment=5}o.get_parameters("chord_accidental_adjust.config.txt",p)function chord_accidental_adjust_up()local q=finale.FCChordPrefs()q:Load(1)local r=q:GetFlatBaselineAdjustment()local s=q:GetSharpBaselineAdjustment()local t=q:GetNaturalBaselineAdjustment()local q=finale.FCChordPrefs()q:Load(1)q:GetFlatBaselineAdjustment()q.FlatBaselineAdjustment=p.vertical_increment+r;q:GetSharpBaselineAdjustment()q.SharpBaselineAdjustment=p.vertical_increment+s;q:GetNaturalBaselineAdjustment()q.NaturalBaselineAdjustment=p.vertical_increment+t;q:Save()end;chord_accidental_adjust_up()end)c("library.configuration",function(require,n,c,d)local u={}function u.finale_version(v,w,x)local y=bit32.bor(bit32.lshift(math.floor(v),24),bit32.lshift(math.floor(w),20))if x then y=bit32.bor(y,math.floor(x))end;return y end;function u.group_overlaps_region(z,A)if A:IsFullDocumentSpan()then return true end;local B=false;local C=finale.FCSystemStaves()C:LoadAllForRegion(A)for D in each(C)do if z:ContainsStaff(D:GetStaff())then B=true;break end end;if not B then return false end;if z.StartMeasure>A.EndMeasure or z.EndMeasure0 then K=true;break end;I=I+1 end;if K then local L=finale.FCStaffSystem()L:Load(J:GetFirstSystem())return finale.FCCell(L.FirstMeasure,L.TopStaff)end;local M=finale.FCMusicRegion()M:SetFullDocument()return finale.FCCell(M.EndMeasure,M.EndStaff)end;function u.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local N=finale.FCMusicRegion()N:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),N.StartStaff)end;return u.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function u.get_top_left_selected_or_visible_cell()local G=finenv.Region()if not G:IsEmpty()then return finale.FCCell(G.StartMeasure,G.StartStaff)end;return u.get_top_left_visible_cell()end;function u.is_default_measure_number_visible_on_cell(O,P,Q,R)local S=finale.FCCurrentStaffSpec()if not S:LoadForCell(P,0)then return false end;if O:GetShowOnTopStaff()and P.Staff==Q.TopStaff then return true end;if O:GetShowOnBottomStaff()and P.Staff==Q:CalcBottomStaff()then return true end;if S.ShowMeasureNumbers then return not O:GetExcludeOtherStaves(R)end;return false end;function u.is_default_number_visible_and_left_aligned(O,P,T,R,U)if O.UseScoreInfoForParts then R=false end;if U and O:GetShowOnMultiMeasureRests(R)then if finale.MNALIGN_LEFT~=O:GetMultiMeasureAlignment(R)then return false end elseif P.Measure==T.FirstMeasure then if not O:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=O:GetStartAlignment(R)then return false end else if not O:GetShowMultiples(R)then return false end;if finale.MNALIGN_LEFT~=O:GetMultipleAlignment(R)then return false end end;return u.is_default_measure_number_visible_on_cell(O,P,T,R)end;function u.update_layout(V,W)V=V or 1;W=W or false;local X=finale.FCPage()if X:Load(V)then X:UpdateLayout(W)end end;function u.get_current_part()local Y=finale.FCParts()Y:LoadAll()return Y:GetCurrent()end;function u.get_page_format_prefs()local Z=u.get_current_part()local _=finale.FCPageFormatPrefs()local a0=false;if Z:IsScore()then a0=_:LoadScore()else a0=_:LoadParts()end;return _,a0 end;function u.get_smufl_metadata_file(a1)if not a1 then a1=finale.FCFontInfo()a1:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local a2=function(a3,a1)local a4=a3 .."/SMuFL/Fonts/"..a1.Name.."/"..a1.Name..".json"return io.open(a4,"r")end;local a5=""if finenv.UI():IsOnWindows()then a5=os.getenv("LOCALAPPDATA")else a5=os.getenv("HOME").."/Library/Application Support"end;local a6=a2(a5,a1)if nil~=a6 then return a6 end;local a7="/Library/Application Support"if finenv.UI():IsOnWindows()then a7=os.getenv("COMMONPROGRAMFILES")end;return a2(a7,a1)end;function u.is_font_smufl_font(a1)if not a1 then a1=finale.FCFontInfo()a1:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=u.finale_version(27,1)then if nil~=a1.IsSMuFLFont then return a1.IsSMuFLFont end end;local a8=u.get_smufl_metadata_file(a1)if nil~=a8 then io.close(a8)return true end;return false end;function u.simple_input(a9,aa)local ab=finale.FCString()ab.LuaString=""local ac=finale.FCString()local ad=160;function format_ctrl(ae,af,ag,ah)ae:SetHeight(af)ae:SetWidth(ag)ac.LuaString=ah;ae:SetText(ac)end;title_width=string.len(a9)*6+54;if title_width>ad then ad=title_width end;text_width=string.len(aa)*6;if text_width>ad then ad=text_width end;ac.LuaString=a9;local ai=finale.FCCustomLuaWindow()ai:SetTitle(ac)local aj=ai:CreateStatic(0,0)format_ctrl(aj,16,ad,aa)local ak=ai:CreateEdit(0,20)format_ctrl(ak,20,ad,"")ai:CreateOkButton()ai:CreateCancelButton()function callback(ae)end;ai:RegisterHandleCommand(callback)if ai:ExecuteModal(nil)==finale.EXECMODAL_OK then ab.LuaString=ak:GetText(ab)return ab.LuaString end end;function u.is_finale_object(al)return al and type(al)=="userdata"and al.ClassName and al.GetClassID and true or false end;function u.system_indent_set_to_prefs(T,_)_=_ or u.get_page_format_prefs()local am=finale.FCMeasure()local an=T.FirstMeasure==1;if not an and am:Load(T.FirstMeasure)then if am.ShowFullNames then an=true end end;if an and _.UseFirstSystemMargins then T.LeftMargin=_.FirstSystemLeft else T.LeftMargin=_.SystemLeft end;return T:Save()end;return u end)return a("__root") \ No newline at end of file diff --git a/dist/chord_baseline_move_down.lua b/dist/chord_baseline_move_down.lua deleted file mode 100644 index 9e1817f7..00000000 --- a/dist/chord_baseline_move_down.lua +++ /dev/null @@ -1,200 +0,0 @@ -function plugindef() - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0.0" - finaleplugin.Date = "March 26, 2022" - finaleplugin.CategoryTags = "Chord" - finaleplugin.AuthorURL = "https://nickmazuk.com" - finaleplugin.Notes = [[ - Select system staves for which you want to move the chord baseline down, then run this script - ]] - return "Move chord baseline down", "Move chord baseline down", "Moves the selected chord baseline down one space" -end - ---[[ -$module measurement -]] -- -local measurement = {} - -local unit_names = { - [finale.MEASUREMENTUNIT_EVPUS] = "EVPUs", - [finale.MEASUREMENTUNIT_INCHES] = "Inches", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "Centimeters", - [finale.MEASUREMENTUNIT_POINTS] = "Points", - [finale.MEASUREMENTUNIT_PICAS] = "Picas", - [finale.MEASUREMENTUNIT_SPACES] = "Spaces", -} - -local unit_suffixes = { - [finale.MEASUREMENTUNIT_EVPUS] = "e", - [finale.MEASUREMENTUNIT_INCHES] = "i", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "c", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "p", - [finale.MEASUREMENTUNIT_SPACES] = "s", -} - -local unit_abbreviations = { - [finale.MEASUREMENTUNIT_EVPUS] = "ev", - [finale.MEASUREMENTUNIT_INCHES] = "in", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "cm", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "pc", - [finale.MEASUREMENTUNIT_SPACES] = "sp", -} - ---[[ -% convert_to_EVPUs - -Converts the specified string into EVPUs. Like text boxes in Finale, this supports -the usage of units at the end of the string. The following are a few examples: - -- `12s` => 288 (12 spaces is 288 EVPUs) -- `8.5i` => 2448 (8.5 inches is 2448 EVPUs) -- `10cm` => 1133 (10 centimeters is 1133 EVPUs) -- `10mm` => 113 (10 millimeters is 113 EVPUs) -- `1pt` => 4 (1 point is 4 EVPUs) -- `2.5p` => 120 (2.5 picas is 120 EVPUs) - -Read the [Finale User Manual](https://usermanuals.finalemusic.com/FinaleMac/Content/Finale/def-equivalents.htm#overriding-global-measurement-units) -for more details about measurement units in Finale. - -@ text (string) the string to convert -: (number) the converted number of EVPUs -]] -function measurement.convert_to_EVPUs(text) - local str = finale.FCString() - str.LuaString = text - return str:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) -end - ---[[ -% get_unit_name - -Returns the name of a measurement unit. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_name(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_names[unit] -end - ---[[ -% get_unit_suffix - -Returns the measurement unit's suffix. Suffixes can be used to force the text value (eg in `FCString` or `FCCtrlEdit`) to be treated as being from a particular measurement unit -Note that although this method returns a "p" for Picas, the fractional part goes after the "p" (eg `1p6`), so in practice it may be that no suffix is needed. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_suffix(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_suffixes[unit] -end - ---[[ -% get_unit_abbreviation - -Returns measurement unit abbreviations that are more human-readable than Finale's internal suffixes. -Abbreviations are also compatible with the internal ones because Finale discards everything after the first letter that isn't part of the suffix. - -For example: -```lua -local str_internal = finale.FCString() -str.LuaString = "2i" - -local str_display = finale.FCString() -str.LuaString = "2in" - -print(str_internal:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) == str_display:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT)) -- true -``` - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_abbreviation(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_abbreviations[unit] -end - ---[[ -% is_valid_unit - -Checks if a number is equal to one of the finale MEASUREMENTUNIT constants. - -@ unit (number) The unit to check. -: (boolean) `true` if valid, `false` if not. -]] -function measurement.is_valid_unit(unit) - return unit_names[unit] and true or false -end - ---[[ -% get_real_default_unit - -Resolves `finale.MEASUREMENTUNIT_DEFAULT` to the value of one of the other `MEASUREMENTUNIT` constants. - -: (number) -]] -function measurement.get_real_default_unit() - local str = finale.FCString() - finenv.UI():GetDecimalSeparator(str) - local separator = str.LuaString - str:SetMeasurement(72, finale.MEASUREMENTUNIT_DEFAULT) - - if str.LuaString == "72" then - return finale.MEASUREMENTUNIT_EVPUS - elseif str.LuaString == "0" .. separator .. "25" then - return finale.MEASUREMENTUNIT_INCHES - elseif str.LuaString == "0" .. separator .. "635" then - return finale.MEASUREMENTUNIT_CENTIMETERS - elseif str.LuaString == "18" then - return finale.MEASUREMENTUNIT_POINTS - elseif str.LuaString == "1p6" then - return finale.MEASUREMENTUNIT_PICAS - elseif str.LuaString == "3" then - return finale.MEASUREMENTUNIT_SPACES - end -end - - - - -function chord_baseline_move_down() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local last_system = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local last_system_number = last_system:GetItemNo() - local start_staff = region:GetStartStaff() - local end_staff = region:GetEndStaff() - - for i = system_number, last_system_number, 1 do - local baselines = finale.FCBaselines() - baselines:LoadAllForSystem(finale.BASELINEMODE_CHORD, i) - for j = start_staff, end_staff, 1 do - local baseline = baselines:AssureSavedStaff(finale.BASELINEMODE_CHORD, i, j) - baseline.VerticalOffset = baseline.VerticalOffset + measurement.convert_to_EVPUs("-1s") - baseline:Save() - end - end -end - -chord_baseline_move_down() diff --git a/dist/chord_baseline_move_up.lua b/dist/chord_baseline_move_up.lua deleted file mode 100644 index e2de1223..00000000 --- a/dist/chord_baseline_move_up.lua +++ /dev/null @@ -1,200 +0,0 @@ -function plugindef() - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0.0" - finaleplugin.Date = "March 26, 2022" - finaleplugin.CategoryTags = "Chord" - finaleplugin.AuthorURL = "https://nickmazuk.com" - finaleplugin.Notes = [[ - Select system staves for which you want to move the chord baseline up, then run this script - ]] - return "Move chord baseline up", "Move chord baseline up", "Moves the selected chord baseline up one space" -end - ---[[ -$module measurement -]] -- -local measurement = {} - -local unit_names = { - [finale.MEASUREMENTUNIT_EVPUS] = "EVPUs", - [finale.MEASUREMENTUNIT_INCHES] = "Inches", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "Centimeters", - [finale.MEASUREMENTUNIT_POINTS] = "Points", - [finale.MEASUREMENTUNIT_PICAS] = "Picas", - [finale.MEASUREMENTUNIT_SPACES] = "Spaces", -} - -local unit_suffixes = { - [finale.MEASUREMENTUNIT_EVPUS] = "e", - [finale.MEASUREMENTUNIT_INCHES] = "i", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "c", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "p", - [finale.MEASUREMENTUNIT_SPACES] = "s", -} - -local unit_abbreviations = { - [finale.MEASUREMENTUNIT_EVPUS] = "ev", - [finale.MEASUREMENTUNIT_INCHES] = "in", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "cm", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "pc", - [finale.MEASUREMENTUNIT_SPACES] = "sp", -} - ---[[ -% convert_to_EVPUs - -Converts the specified string into EVPUs. Like text boxes in Finale, this supports -the usage of units at the end of the string. The following are a few examples: - -- `12s` => 288 (12 spaces is 288 EVPUs) -- `8.5i` => 2448 (8.5 inches is 2448 EVPUs) -- `10cm` => 1133 (10 centimeters is 1133 EVPUs) -- `10mm` => 113 (10 millimeters is 113 EVPUs) -- `1pt` => 4 (1 point is 4 EVPUs) -- `2.5p` => 120 (2.5 picas is 120 EVPUs) - -Read the [Finale User Manual](https://usermanuals.finalemusic.com/FinaleMac/Content/Finale/def-equivalents.htm#overriding-global-measurement-units) -for more details about measurement units in Finale. - -@ text (string) the string to convert -: (number) the converted number of EVPUs -]] -function measurement.convert_to_EVPUs(text) - local str = finale.FCString() - str.LuaString = text - return str:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) -end - ---[[ -% get_unit_name - -Returns the name of a measurement unit. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_name(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_names[unit] -end - ---[[ -% get_unit_suffix - -Returns the measurement unit's suffix. Suffixes can be used to force the text value (eg in `FCString` or `FCCtrlEdit`) to be treated as being from a particular measurement unit -Note that although this method returns a "p" for Picas, the fractional part goes after the "p" (eg `1p6`), so in practice it may be that no suffix is needed. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_suffix(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_suffixes[unit] -end - ---[[ -% get_unit_abbreviation - -Returns measurement unit abbreviations that are more human-readable than Finale's internal suffixes. -Abbreviations are also compatible with the internal ones because Finale discards everything after the first letter that isn't part of the suffix. - -For example: -```lua -local str_internal = finale.FCString() -str.LuaString = "2i" - -local str_display = finale.FCString() -str.LuaString = "2in" - -print(str_internal:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) == str_display:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT)) -- true -``` - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_abbreviation(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_abbreviations[unit] -end - ---[[ -% is_valid_unit - -Checks if a number is equal to one of the finale MEASUREMENTUNIT constants. - -@ unit (number) The unit to check. -: (boolean) `true` if valid, `false` if not. -]] -function measurement.is_valid_unit(unit) - return unit_names[unit] and true or false -end - ---[[ -% get_real_default_unit - -Resolves `finale.MEASUREMENTUNIT_DEFAULT` to the value of one of the other `MEASUREMENTUNIT` constants. - -: (number) -]] -function measurement.get_real_default_unit() - local str = finale.FCString() - finenv.UI():GetDecimalSeparator(str) - local separator = str.LuaString - str:SetMeasurement(72, finale.MEASUREMENTUNIT_DEFAULT) - - if str.LuaString == "72" then - return finale.MEASUREMENTUNIT_EVPUS - elseif str.LuaString == "0" .. separator .. "25" then - return finale.MEASUREMENTUNIT_INCHES - elseif str.LuaString == "0" .. separator .. "635" then - return finale.MEASUREMENTUNIT_CENTIMETERS - elseif str.LuaString == "18" then - return finale.MEASUREMENTUNIT_POINTS - elseif str.LuaString == "1p6" then - return finale.MEASUREMENTUNIT_PICAS - elseif str.LuaString == "3" then - return finale.MEASUREMENTUNIT_SPACES - end -end - - - - -function chord_baseline_move_down() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local last_system = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local last_system_number = last_system:GetItemNo() - local start_staff = region:GetStartStaff() - local end_staff = region:GetEndStaff() - - for i = system_number, last_system_number, 1 do - local baselines = finale.FCBaselines() - baselines:LoadAllForSystem(finale.BASELINEMODE_CHORD, i) - for j = start_staff, end_staff, 1 do - local baseline = baselines:AssureSavedStaff(finale.BASELINEMODE_CHORD, i, j) - baseline.VerticalOffset = baseline.VerticalOffset + measurement.convert_to_EVPUs("1s") - baseline:Save() - end - end -end - -chord_baseline_move_down() diff --git a/dist/chord_toggle_visibility.lua b/dist/chord_toggle_visibility.lua index ff7c55fd..68e62dbb 100644 --- a/dist/chord_toggle_visibility.lua +++ b/dist/chord_toggle_visibility.lua @@ -1,23 +1 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0" - finaleplugin.Date = "July 2, 2019" - finaleplugin.CategoryTags = "Chord" - finaleplugin.AuthorURL = "https://nickmazuk.com" - return "Toggle Chord Visibility 2", "Toggle Chord Visibility 2", "Toggles the chords' visibility" -end - -function chord_toggle_visibility() - local musicRegion = finenv.Region() - musicRegion:SetCurrentSelection() - local chords = finale.FCChords() - chords:LoadAllForRegion(musicRegion) - for chord in each(chords) do - chord.ChordVisible = not chord:GetChordVisible() - chord:Save() - end -end - -chord_toggle_visibility() +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Nick Mazuk"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0"finaleplugin.Date="July 2, 2019"finaleplugin.CategoryTags="Chord"finaleplugin.AuthorURL="https://nickmazuk.com"return"Toggle Chord Visibility 2","Toggle Chord Visibility 2","Toggles the chords' visibility"end;function chord_toggle_visibility()local a=finenv.Region()a:SetCurrentSelection()local b=finale.FCChords()b:LoadAllForRegion(a)for c in each(b)do c.ChordVisible=not c:GetChordVisible()c:Save()end end;chord_toggle_visibility() \ No newline at end of file diff --git a/dist/cross_staff_offset.lua b/dist/cross_staff_offset.lua index f494e76e..291f8a15 100644 --- a/dist/cross_staff_offset.lua +++ b/dist/cross_staff_offset.lua @@ -1,11 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Author = "Carl Vine" - finaleplugin.AuthorURL = "http://carlvine.com" - finaleplugin.Version = "v1.21" - finaleplugin.Date = "2022/05/23" - finaleplugin.Notes = [[ +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Author="Carl Vine"finaleplugin.AuthorURL="http://carlvine.com"finaleplugin.Version="v1.21"finaleplugin.Date="2022/05/23"finaleplugin.Notes=[[ When creating cross-staff notes using the option-downarrow shortcut, the stems of 'crossed' notes are reversed (on the wrong side of the notehead) and so appear too far to the right (if shifting downwards) by the width of one notehead (typically 24EVPU). This script lets you set a horizontal offset for all cross-staff @@ -16,93 +9,4 @@ function plugindef() If you invoke the plugin with Option key (macOS) or Shift key pressed, the script uses the last settings from the dialog. - ]] - return "CrossStaff Offset", "CrossStaff Offset", "Offset horizontal position of cross-staff note entries" -end - -function user_selects_offset() - local current_vert, vertical_step = 10, 25 - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 -- extra y-offset for Mac text box - local edit_box_horiz = 120 - - local dialog = finale.FCCustomWindow() - local str = finale.FCString() - str.LuaString = plugindef() - dialog:SetTitle(str) - - local answer = {} - local texts = { -- words, default value - { "Cross-staff offset:", cross_offset and cross_offset or 0 }, --> answer[1] cross_offset - { "Non-crossed offset:", non_cross_offset and non_cross_offset or 0 }, --> answer[2] non_cross_offset - { "Layer 1-4 (0 = all):", layer_number and layer_number or 0 } --> answer[3] layer_number - } - - for i,v in ipairs(texts) do - str.LuaString = v[1] - local static = dialog:CreateStatic(0, current_vert) - static:SetText(str) - static:SetWidth(200) - answer[i] = dialog:CreateEdit(edit_box_horiz, current_vert - mac_offset) - answer[i]:SetInteger(v[2]) - answer[i]:SetWidth(75) - if i < 3 then - str.LuaString = "EVPUs" - dialog:CreateStatic(edit_box_horiz + 80, current_vert):SetText(str) - end - current_vert = current_vert + vertical_step - end - - local static = dialog:CreateStatic(0, current_vert + 8) - str.LuaString = "cross to staff below = [ -24 | 0 ] or [ -12, 12 ]" - static:SetText(str) - static:SetWidth(240) - static = dialog:CreateStatic(0, current_vert + vertical_step) - str.LuaString = "cross to staff above = [ 24 | 0 ] or [ 12, -12 ]" - static:SetText(str) - static:SetWidth(240) - - dialog:CreateOkButton() - dialog:CreateCancelButton() - return dialog:ExecuteModal(nil), -- == 1 if Ok button pressed, else 0 - answer[1]:GetInteger(), answer[2]:GetInteger(), answer[3]:GetInteger() -end - -function is_out_of_range(horiz_offset) - max_value = 999 -- some unrealistic horizontal offset (EVPUs) - return ( horiz_offset > max_value or horiz_offset < max_value * -1 ) -end - -function cross_staff_offset() - local modifier_keys_pressed = finenv.QueryInvokedModifierKeys and - (finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_ALT) or finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_SHIFT)) - if not modifier_keys_pressed or dialog_result ~= finale.EXECMODAL_OK then - -- use global result values so that RetainLuaState remembers them - dialog_result, cross_offset, non_cross_offset, layer_number = user_selects_offset() - if dialog_result ~= finale.EXECMODAL_OK then -- user cancelled - return - end - if nil ~= finenv.RetainLuaState then - finenv.RetainLuaState = true - end - end - - local error = "" -- TEST FOR ERRORS (note that all user responses are perforce already integers) - if layer_number < 0 or layer_number > 4 then - error = "The layer number must\nbe between 0 and 4\n(not " .. layer_number .. ")" - elseif is_out_of_range(cross_offset) or is_out_of_range(non_cross_offset) then - error = "Choose realistic offset\nvalues (say from -999 to 999)\n(not " .. cross_offset .. " / " .. non_cross_offset .. ")" - end - if (error ~= "") then -- error dialog and exit - finenv.UI():AlertNeutral("script: "..plugindef(), error) - dialog_result = finale.EXECMODAL_CANCEL -- force dialog box to open next time - else - -- change entry offsets - for entry in eachentrysaved(finenv.Region(), layer_number) do -- only access notes in the chosen layer (0 = all layers) - if entry:IsNote() then - entry.ManualPosition = entry.CrossStaff and cross_offset or non_cross_offset - end - end - end -end - -cross_staff_offset() + ]]return"CrossStaff Offset","CrossStaff Offset","Offset horizontal position of cross-staff note entries"end;function user_selects_offset()local a,b=10,25;local c=finenv.UI():IsOnMac()and 3 or 0;local d=120;local e=finale.FCCustomWindow()local f=finale.FCString()f.LuaString=plugindef()e:SetTitle(f)local g={}local h={{"Cross-staff offset:",cross_offset and cross_offset or 0},{"Non-crossed offset:",non_cross_offset and non_cross_offset or 0},{"Layer 1-4 (0 = all):",layer_number and layer_number or 0}}for i,j in ipairs(h)do f.LuaString=j[1]local k=e:CreateStatic(0,a)k:SetText(f)k:SetWidth(200)g[i]=e:CreateEdit(d,a-c)g[i]:SetInteger(j[2])g[i]:SetWidth(75)if i<3 then f.LuaString="EVPUs"e:CreateStatic(d+80,a):SetText(f)end;a=a+b end;local k=e:CreateStatic(0,a+8)f.LuaString="cross to staff below = [ -24 | 0 ] or [ -12, 12 ]"k:SetText(f)k:SetWidth(240)k=e:CreateStatic(0,a+b)f.LuaString="cross to staff above = [ 24 | 0 ] or [ 12, -12 ]"k:SetText(f)k:SetWidth(240)e:CreateOkButton()e:CreateCancelButton()return e:ExecuteModal(nil),g[1]:GetInteger(),g[2]:GetInteger(),g[3]:GetInteger()end;function is_out_of_range(l)max_value=999;return l>max_value or l4 then n="The layer number must\nbe between 0 and 4\n(not "..layer_number..")"elseif is_out_of_range(cross_offset)or is_out_of_range(non_cross_offset)then n="Choose realistic offset\nvalues (say from -999 to 999)\n(not "..cross_offset.." / "..non_cross_offset..")"end;if n~=""then finenv.UI():AlertNeutral("script: "..plugindef(),n)dialog_result=finale.EXECMODAL_CANCEL else for o in eachentrysaved(finenv.Region(),layer_number)do if o:IsNote()then o.ManualPosition=o.CrossStaff and cross_offset or non_cross_offset end end end end;cross_staff_offset() \ No newline at end of file diff --git a/dist/cue_notes_create.lua b/dist/cue_notes_create.lua index 3c40325a..9fbea446 100644 --- a/dist/cue_notes_create.lua +++ b/dist/cue_notes_create.lua @@ -1,939 +1,8 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Carl Vine" - finaleplugin.AuthorURL = "http://carlvine.com" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "v0.63" - finaleplugin.Date = "2022/05/22" - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Carl Vine"finaleplugin.AuthorURL="http://carlvine.com"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="v0.63"finaleplugin.Date="2022/05/22"finaleplugin.Notes=[[ This script is keyboard-centric requiring minimal mouse action. It takes music in Layer 1 from one staff in the selected region and creates a "Cue" version on another chosen staff. The cue copy is reduced in size and muted, and can duplicate chosen markings from the original. It is shifted to the chosen layer with a (real) whole-note rest placed in layer 1. Your preferences are saved after each script run in a "config" file inside a folder called "script_settings" in the same file location as this script. If your choices aren't being saved then you need to create that folder. If that still doesn't work you need to use another folder for your Lua scripts that has fewer access restrictions. If using RGPLua (v0.58 or later) the script automatically creates a new expression category called "Cue Names" if it does not exist. If using JWLua, before running the script you must create an Expression Category called "Cue Names" containing at least one text expression. - ]] - return "Cue Notes Create", "Cue Notes Create", "Copy as cue notes to another staff" -end - --- Author: Robert Patterson --- Date: March 5, 2021 ---[[ -$module Configuration - -This library implements a UTF-8 text file scheme for configuration and user settings as follows: - -- Comments start with `--` -- Leading, trailing, and extra whitespace is ignored -- Each parameter is named and delimited as follows: - -``` - = -``` - -Parameter values may be: - -- Strings delimited with either single- or double-quotes -- Tables delimited with `{}` that may contain strings, booleans, or numbers -- Booleans (`true` or `false`) -- Numbers - -Currently the following are not supported: - -- Tables embedded within tables -- Tables containing strings that contain commas - -A sample configuration file might be: - -```lua --- Configuration File for "Hairpin and Dynamic Adjustments" script --- -left_dynamic_cushion = 12 --evpus -right_dynamic_cushion = -6 --evpus -``` - -## Configuration Files - -Configuration files provide a way for power users to modify script behavior without -having to modify the script itself. Some users track their changes to their configuration files, -so scripts should not create or modify them programmatically. - -- The user creates each configuration file in a subfolder called `script_settings` within -the folder of the calling script. -- Each script that has a configuration file defines its own configuration file name. -- It is entirely appropriate over time for scripts to transition from configuration files to user settings, -but this requires implementing a user interface to modify the user settings from within the script. -(See below.) - -## User Settings Files - -User settings are written by the scripts themselves and reside in the user's preferences folder -in an appropriately-named location for the operating system. (The naming convention is a detail that the -configuration library handles for the caller.) If the user settings are to be changed from their defaults, -the script itself should provide a means to change them. This could be a (preferably optional) dialog box -or any other mechanism the script author chooses. - -User settings are saved in the user's preferences folder (on Mac) or AppData folder (on Windows). - -## Merge Process - -Files are _merged_ into the passed-in list of default values. They do not _replace_ the list. Each calling script contains -a table of all the configurable parameters or settings it recognizes along with default values. An example: - -`sample.lua:` - -```lua -parameters = { - x = 1, - y = 2, - z = 3 -} - -configuration.get_parameters(parameters, "script.config.txt") - -for k, v in pairs(parameters) do - print(k, v) -end -``` - -Suppose the `script.config.text` file is as follows: - -``` -y = 4 -q = 6 -``` - -The returned parameters list is: - - -```lua -parameters = { - x = 1, -- remains the default value passed in - y = 4, -- replaced value from the config file - z = 3 -- remains the default value passed in -} -``` - -The `q` parameter in the config file is ignored because the input paramater list -had no `q` parameter. - -This approach allows total flexibility for the script add to or modify its list of parameters -without having to worry about older configuration files or user settings affecting it. -]] - -local configuration = {} - -local script_settings_dir = "script_settings" -- the parent of this directory is the running lua path -local comment_marker = "--" -local parameter_delimiter = "=" -local path_delimiter = "/" - -local file_exists = function(file_path) - local f = io.open(file_path, "r") - if nil ~= f then - io.close(f) - return true - end - return false -end - -local strip_leading_trailing_whitespace = function(str) - return str:match("^%s*(.-)%s*$") -- lua pattern magic taken from the Internet -end - -local parse_table = function(val_string) - local ret_table = {} - for element in val_string:gmatch("[^,%s]+") do -- lua pattern magic taken from the Internet - local parsed_element = parse_parameter(element) - table.insert(ret_table, parsed_element) - end - return ret_table -end - -parse_parameter = function(val_string) - if "\"" == val_string:sub(1, 1) and "\"" == val_string:sub(#val_string, #val_string) then -- double-quote string - return string.gsub(val_string, "\"(.+)\"", "%1") -- lua pattern magic: "(.+)" matches all characters between two double-quote marks (no escape chars) - elseif "'" == val_string:sub(1, 1) and "'" == val_string:sub(#val_string, #val_string) then -- single-quote string - return string.gsub(val_string, "'(.+)'", "%1") -- lua pattern magic: '(.+)' matches all characters between two single-quote marks (no escape chars) - elseif "{" == val_string:sub(1, 1) and "}" == val_string:sub(#val_string, #val_string) then - return parse_table(string.gsub(val_string, "{(.+)}", "%1")) - elseif "true" == val_string then - return true - elseif "false" == val_string then - return false - end - return tonumber(val_string) -end - -local get_parameters_from_file = function(file_path, parameter_list) - local file_parameters = {} - - if not file_exists(file_path) then - return false - end - - for line in io.lines(file_path) do - local comment_at = string.find(line, comment_marker, 1, true) -- true means find raw string rather than lua pattern - if nil ~= comment_at then - line = string.sub(line, 1, comment_at - 1) - end - local delimiter_at = string.find(line, parameter_delimiter, 1, true) - if nil ~= delimiter_at then - local name = strip_leading_trailing_whitespace(string.sub(line, 1, delimiter_at - 1)) - local val_string = strip_leading_trailing_whitespace(string.sub(line, delimiter_at + 1)) - file_parameters[name] = parse_parameter(val_string) - end - end - - for param_name, _ in pairs(parameter_list) do - local param_val = file_parameters[param_name] - if nil ~= param_val then - parameter_list[param_name] = param_val - end - end - - return true -end - ---[[ -% get_parameters - -Searches for a file with the input filename in the `script_settings` directory and replaces the default values in `parameter_list` -with any that are found in the config file. - -@ file_name (string) the file name of the config file (which will be prepended with the `script_settings` directory) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true if the file exists -]] -function configuration.get_parameters(file_name, parameter_list) - local path = "" - if finenv.IsRGPLua then - path = finenv.RunningLuaFolderPath() - else - local str = finale.FCString() - str:SetRunningLuaFolderPath() - path = str.LuaString - end - local file_path = path .. script_settings_dir .. path_delimiter .. file_name - return get_parameters_from_file(file_path, parameter_list) -end - --- Calculates a filepath in the user's preferences folder using recommended naming conventions --- -local calc_preferences_filepath = function(script_name) - local str = finale.FCString() - str:SetUserOptionsPath() - local folder_name = str.LuaString - if not finenv.IsRGPLua and finenv.UI():IsOnMac() then - -- works around bug in SetUserOptionsPath() in JW Lua - folder_name = os.getenv("HOME") .. folder_name:sub(2) -- strip '~' and replace with actual folder - end - if finenv.UI():IsOnWindows() then - folder_name = folder_name .. path_delimiter .. "FinaleLua" - end - local file_path = folder_name .. path_delimiter - if finenv.UI():IsOnMac() then - file_path = file_path .. "com.finalelua." - end - file_path = file_path .. script_name .. ".settings.txt" - return file_path, folder_name -end - ---[[ -% save_user_settings - -Saves the user's preferences for a script from the values provided in `parameter_list`. - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true on success -]] -function configuration.save_user_settings(script_name, parameter_list) - local file_path, folder_path = calc_preferences_filepath(script_name) - local file = io.open(file_path, "w") - if not file and finenv.UI():IsOnWindows() then -- file not found - os.execute('mkdir "' .. folder_path ..'"') -- so try to make a folder (windows only, since the folder is guaranteed to exist on mac) - file = io.open(file_path, "w") -- try the file again - end - if not file then -- still couldn't find file - return false -- so give up - end - file:write("-- User settings for " .. script_name .. ".lua\n\n") - for k,v in pairs(parameter_list) do -- only number, boolean, or string values - if type(v) == "string" then - v = "\"" .. v .."\"" - else - v = tostring(v) - end - file:write(k, " = ", v, "\n") - end - file:close() - return true -- success -end - ---[[ -% get_user_settings - -Find the user's settings for a script in the preferences directory and replaces the default values in `parameter_list` -with any that are found in the preferences file. The actual name and path of the preferences file is OS dependent, so -the input string should just be the script name (without an extension). - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -@ [create_automatically] (boolean) if true, create the file automatically (default is `true`) -: (boolean) `true` if the file already existed, `false` if it did not or if it was created automatically -]] -function configuration.get_user_settings(script_name, parameter_list, create_automatically) - if create_automatically == nil then create_automatically = true end - local exists = get_parameters_from_file(calc_preferences_filepath(script_name), parameter_list) - if not exists and create_automatically then - configuration.save_user_settings(script_name, parameter_list) - end - return exists -end - - - ---[[ -$module Clef - -A library of general clef utility functions. -]] -- -local clef = {} - ---[[ -% get_cell_clef - -Gets the clef for any cell. - -@ measure (number) The measure number for the cell -@ staff_number (number) The staff number for the cell -: (number) The clef for the cell -]] -function clef.get_cell_clef(measure, staff_number) - local cell_clef = -1 - local cell = finale.FCCell(measure, staff_number) - local cell_frame_hold = finale.FCCellFrameHold() - - cell_frame_hold:ConnectCell(cell) - if cell_frame_hold:Load() then - if cell_frame_hold.IsClefList then - cell_clef = cell_frame_hold:CreateFirstCellClefChange().ClefIndex - else - cell_clef = cell_frame_hold.ClefIndex - end - end - return cell_clef -end - ---[[ -% get_default_clef - -Gets the default clef for any staff for a specific region. - -@ first_measure (number) The first measure of the region -@ last_measure (number) The last measure of the region -@ staff_number (number) The staff number for the cell -: (number) The default clef for the staff -]] -function clef.get_default_clef(first_measure, last_measure, staff_number) - local staff = finale.FCStaff() - local cell_clef = clef.get_cell_clef(first_measure - 1, staff_number) - if cell_clef < 0 then -- failed, so check clef AFTER insertion - cell_clef = clef.get_cell_clef(last_measure + 1, staff_number) - if cell_clef < 0 then -- resort to destination staff default clef - cell_clef = staff:Load(staff_number) and staff.DefaultClef or 0 -- default treble - end - end - return cell_clef -end - ---[[ -% can_change_clef - -Determine if the current version of the plugin can change clefs. - -: (boolean) Whether or not the plugin can change clefs -]] -function clef.can_change_clef() - -- RGPLua 0.60 or later needed for clef changing - return finenv.IsRGPLua or finenv.StringVersion >= "0.60" -end - ---[[ -% restore_default_clef - -Restores the default clef for any staff for a specific region. - -@ first_measure (number) The first measure of the region -@ last_measure (number) The last measure of the region -@ staff_number (number) The staff number for the cell -]] -function clef.restore_default_clef(first_measure, last_measure, staff_number) - if not clef.can_change_clef() then - return - end - - local default_clef = clef.get_default_clef(first_measure, last_measure, staff_number) - - for measure = first_measure, last_measure do - local cell = finale.FCCell(measure, staff_number) - local cell_frame_hold = finale.FCCellFrameHold() - cell_frame_hold:ConnectCell(cell) - if cell_frame_hold:Load() then - cell_frame_hold:MakeCellSingleClef(nil) -- RGPLua v0.60 - cell_frame_hold:SetClefIndex(default_clef) - cell_frame_hold:Save() - end - end -end - - - ---[[ -$module Layer -]] -- -local layer = {} - ---[[ -% copy - -Duplicates the notes from the source layer to the destination. The source layer remains untouched. - -@ region (FCMusicRegion) the region to be copied -@ source_layer (number) the number (1-4) of the layer to duplicate -@ destination_layer (number) the number (1-4) of the layer to be copied to -]] -function layer.copy(region, source_layer, destination_layer) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - source_layer = source_layer - 1 - destination_layer = destination_layer - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentry_source_layer = finale.FCNoteEntryLayer(source_layer, staffNum, start, stop) - noteentry_source_layer:Load() - local noteentry_destination_layer = noteentry_source_layer:CreateCloneEntries( - destination_layer, staffNum, start) - noteentry_destination_layer:Save() - noteentry_destination_layer:CloneTuplets(noteentry_source_layer) - noteentry_destination_layer:Save() - end -end -- function layer_copy - ---[[ -% clear - -Clears all entries from a given layer. - -@ region (FCMusicRegion) the region to be cleared -@ layer_to_clear (number) the number (1-4) of the layer to clear -]] -function layer.clear(region, layer_to_clear) - layer_to_clear = layer_to_clear - 1 -- Turn 1 based layer to 0 based layer - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer = finale.FCNoteEntryLayer(layer_to_clear, staffNum, start, stop) - noteentrylayer:Load() - noteentrylayer:ClearAllEntries() - end -end - ---[[ -% swap - -Swaps the entries from two different layers (e.g. 1-->2 and 2-->1). - -@ region (FCMusicRegion) the region to be swapped -@ swap_a (number) the number (1-4) of the first layer to be swapped -@ swap_b (number) the number (1-4) of the second layer to be swapped -]] -function layer.swap(region, swap_a, swap_b) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - -- Set layers for 0 based - swap_a = swap_a - 1 - swap_b = swap_b - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer_1 = finale.FCNoteEntryLayer(swap_a, staffNum, start, stop) - noteentrylayer_1:Load() - noteentrylayer_1.LayerIndex = swap_b - -- - local noteentrylayer_2 = finale.FCNoteEntryLayer(swap_b, staffNum, start, stop) - noteentrylayer_2:Load() - noteentrylayer_2.LayerIndex = swap_a - noteentrylayer_1:Save() - noteentrylayer_2:Save() - end -end - - - - -local config = { -- retained and over-written by the config file, if present - copy_articulations = false, - copy_expressions = false, - copy_smartshapes = false, - copy_slurs = true, - copy_clef = false, - mute_cuenotes = true, - cuenote_percent = 70, -- (75% too big, 66% too small) - cuenote_layer = 3, - freeze_up_down = 0, -- "0" for no freezing, "1" for up, "2" for down - -- if creating a new "Cue Names" category ... - cue_category_name = "Cue Names", - cue_font_smaller = 1, -- how many points smaller than the standard technique expression - prefs_folder = "script_settings", - prefs_file = "cue_notes_create.config", -} - -configuration.get_parameters(config.prefs_file, config) - -function save_config_file() - local folder_path = finenv:RunningLuaFolderPath() .. config.prefs_folder - local file_path = folder_path .. '/' .. config.prefs_file - local file = io.open(file_path, "w") - if nil == file then -- couldn't find file - os.execute('mkdir "' .. folder_path ..'"') -- so make a folder - file = io.open(file_path, "w") -- try again - if nil == file then -- still couldn't find file - return -- give up - end - end - for i,v in pairs(config) do - if type(v) == "boolean" then - v = v and "true" or "false" - elseif type(v) == "string" then - v = '"' .. v ..'"' - end - file:write(i, " = ", v, "\n") - end - file:close() -end - -function show_error(error_code) - local errors = { - only_one_staff = "Please select just one staff\n as the source for the new cue", - empty_region = "Please select a region\nwith some notes in it!", - first_make_expression_category = "You must first create a new Text Expression Category called \""..config.cue_category_name.."\" containing at least one entry", - } - finenv.UI():AlertNeutral("script: " .. plugindef(), errors[error_code]) - return -1 -end - -function should_overwrite_existing_music() - local alert = finenv.UI():AlertOkCancel("script: " .. plugindef(), "Overwrite existing music?") - local should_overwrite = (alert == 0) - return should_overwrite -end - -function region_is_empty(region) - for entry in eachentry(region) do - if entry.Count > 0 then - return false - end - end - return true -end - -function new_cue_name(source_staff) - local dialog = finale.FCCustomWindow() - local str = finale.FCString() - str.LuaString = plugindef() - dialog:SetTitle(str) - - str.LuaString = "New cue name:" - dialog:CreateStatic(0, 20):SetText(str) - - local the_name = dialog:CreateEdit(0, 40) - the_name:SetWidth(200) - -- copy default name from the source Staff Name - local staff = finale.FCStaff() - staff:Load(source_staff) - the_name:SetText( staff:CreateDisplayFullNameString() ) - - dialog:CreateOkButton() - dialog:CreateCancelButton() - local ok = (dialog:ExecuteModal(nil) == finale.EXECMODAL_OK) - the_name:GetText(str) - return ok, str.LuaString -end - -function choose_name_index(name_list) - local dialog = finale.FCCustomWindow() - local str = finale.FCString() - str.LuaString = plugindef() - dialog:SetTitle(str) - str.LuaString = "Select cue name:" - dialog:CreateStatic(0, 20):SetText(str) - - local staff_list = dialog:CreateListBox(0, 40) - staff_list:SetWidth(200) - -- item "0" in the list is "*** new name ***" - str.LuaString = "*** new name ***" - staff_list:AddString(str) - - -- add all names in the extant list - for i,v in ipairs(name_list) do - str.LuaString = v[1] -- copy the name, not the ItemNo - staff_list:AddString(str) - end - dialog:CreateOkButton() - dialog:CreateCancelButton() - return (dialog:ExecuteModal(nil) == finale.EXECMODAL_OK), staff_list:GetSelectedItem() - -- NOTE: returns the chosen INDEX number (0-based) -end - -function create_new_expression(exp_name, category_number) - local cat_def = finale.FCCategoryDef() - cat_def:Load(category_number) - local tfi = cat_def:CreateTextFontInfo() - local str = finale.FCString() - str.LuaString = "^fontTxt" - .. tfi:CreateEnigmaString(finale.FCString()).LuaString - .. exp_name - local ted = finale.FCTextExpressionDef() - ted:SaveNewTextBlock(str) - ted:AssignToCategory(cat_def) - ted:SetUseCategoryPos(true) - ted:SetUseCategoryFont(true) - ted:SaveNew() - return ted:GetItemNo() -- *** RETURNS the new expression's ITEM NUMBER -end - -function choose_destination_staff(source_staff) - local staff_list = {} -- compile all staves in the score - local rgn = finenv.Region() - -- compile staff list by slot number - local original_slot = rgn.StartSlot - rgn:SetFullMeasureStack() -- scan the whole stack - local staff = finale.FCStaff() - for slot = rgn.StartSlot, rgn.EndSlot do - local staff_number = rgn:CalcStaffNumber(slot) - if staff_number ~= source_staff then - staff:Load(staff_number) -- staff at this slot - table.insert(staff_list, { staff_number, staff:CreateDisplayFullNameString().LuaString } ) - end - end - rgn.StartSlot = original_slot -- restore original single staff - rgn.EndSlot = original_slot - - -- draw up the dialog box - local horiz_grid = { 210, 310, 360 } - local vert_step = 20 - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 -- extra horizontal offset for Mac edit boxes - local user_checks = { -- boolean config values - copy choices from CONFIG file - "copy_articulations", "copy_expressions", "copy_smartshapes", - "copy_slurs", "copy_clef", "mute_cuenotes", - -- integer config values - copy choices from CONFIG file - "cuenote_percent", "cuenote_layer", "freeze_up_down" - } - local boolean_count = 6 -- higher than this number are integer config values, not checkboxes - local user_selections = {} -- an array of controls corresponding to user choices - - local str = finale.FCString() - local dialog = finale.FCCustomLuaWindow() - str.LuaString = plugindef() - dialog:SetTitle(str) - local static = dialog:CreateStatic(0, 0) - str.LuaString = "Select destination staff:" - static:SetText(str) - static:SetWidth(200) - - local list_box = dialog:CreateListBox(0, vert_step) - list_box.UseCheckboxes = true - list_box:SetWidth(200) - for i,v in ipairs(staff_list) do -- list all staff names - str.LuaString = v[2] - list_box:AddString(str) - end - -- add user options - str.LuaString = "Cue Options:" - dialog:CreateStatic(horiz_grid[1], 0):SetText(str) - - for i,v in ipairs(user_checks) do -- run through config parameters - str.LuaString = string.gsub(v, '_', ' ') - if i <= boolean_count then -- boolean checkbox - user_selections[i] = dialog:CreateCheckbox(horiz_grid[1], i * vert_step) - user_selections[i]:SetText(str) - user_selections[i]:SetWidth(120) - local checked = config[v] and 1 or 0 - user_selections[i]:SetCheck(checked) - elseif i < #user_checks then -- integer value (#user_checks = stem_direction_popup) - str.LuaString = str.LuaString .. ":" - dialog:CreateStatic(horiz_grid[1], i * vert_step):SetText(str) - user_selections[i] = dialog:CreateEdit(horiz_grid[2], (i * vert_step) - mac_offset) - user_selections[i]:SetInteger(config[v]) - user_selections[i]:SetWidth(50) - end - end - -- popup for stem direction - local stem_direction_popup = dialog:CreatePopup(horiz_grid[1], (#user_checks * vert_step) + 5) - str.LuaString = "Stems: natural direction" - stem_direction_popup:AddString(str) -- config.freeze_up_down == 0 - str.LuaString = "Stems: freeze up" - stem_direction_popup:AddString(str) -- config.freeze_up_down == 1 - str.LuaString = "Stems: freeze down" - stem_direction_popup:AddString(str) -- config.freeze_up_down == 2 - stem_direction_popup:SetWidth(160) - stem_direction_popup:SetSelectedItem(config.freeze_up_down) -- 0-based index - - -- "CLEAR ALL" button to clear copy choices - local clear_button = dialog:CreateButton(horiz_grid[3], vert_step * 2) - str.LuaString = "Clear All" - clear_button:SetWidth(80) - clear_button:SetText(str) - dialog:RegisterHandleControlEvent ( clear_button, - function() - for i = 1, boolean_count do - user_selections[i]:SetCheck(0) - end - list_box:SetKeyboardFocus() - end - ) - - -- "SET ALL" button to set all copy choices - local set_button = dialog:CreateButton(horiz_grid[3], vert_step * 4) - str.LuaString = "Set All" - set_button:SetWidth(80) - set_button:SetText(str) - dialog:RegisterHandleControlEvent ( set_button, - function() - for i = 1, boolean_count do - user_selections[i]:SetCheck(1) - end - list_box:SetKeyboardFocus() - end - ) - -- run the dialog - dialog:CreateOkButton() - dialog:CreateCancelButton() - local ok = (dialog:ExecuteModal(nil) == finale.EXECMODAL_OK) - - local selected_item = list_box:GetSelectedItem() -- retrieve user staff selection (index base 0) - local chosen_staff_number = staff_list[selected_item + 1][1] - - -- save User Pref changes - for i,v in ipairs(user_checks) do -- run through config parameters - if i <= boolean_count then - config[v] = (user_selections[i]:GetCheck() == 1) -- "true" for value 1, checked - elseif i < #user_checks then -- integer value (#user_checks = stem_direction_popup) - local answer = user_selections[i]:GetInteger() - if i == #user_selections and (answer < 2 or answer > 4) then -- legitimate layer number choice? - answer = 4 -- make sure layer number is in range - end - config[v] = answer - end - end - config.freeze_up_down = stem_direction_popup:GetSelectedItem() -- 0-based index - - return ok, chosen_staff_number -end - -function fix_text_expressions(region) - local expressions = finale.FCExpressions() - expressions:LoadAllForRegion(region) - for expression in eachbackwards(expressions) do - if expression.StaffGroupID == 0 then -- staff-attached expressions only - if config.copy_expressions then -- keep them and switch to cuenote layer - expression.LayerAssignment = config.cuenote_layer - expression.ScaleWithEntry = true -- and scale to smaller noteheads - expression:Save() - else - expression:DeleteData() -- otherwise delete them - end - end - end -end - -function copy_to_destination(source_region, destination_staff) - local destination_region = finale.FCMusicRegion() - destination_region:SetRegion(source_region) - destination_region:CopyMusic() -- copy the original - destination_region.StartStaff = destination_staff - destination_region.EndStaff = destination_staff - - if not region_is_empty(destination_region) and (not should_overwrite_existing_music()) then - destination_region:ReleaseMusic() -- clear memory - return false -- and go home - end - -- otherwise carry on ... - destination_region:PasteMusic() -- paste the copy - destination_region:ReleaseMusic() -- and release memory - for layer_number = 2, 4 do -- clear out LAYERS 2-4 - layer.clear(destination_region, layer_number) - end - - -- mute / set to % size / delete articulations / freeze stems - for entry in eachentrysaved(destination_region) do - if entry:IsNote() and config.mute_cuenotes then - entry.Playback = false - end - entry:SetNoteDetailFlag(true) - local entry_mod = finale.FCEntryAlterMod() - entry_mod:SetNoteEntry(entry) - entry_mod:SetResize(config.cuenote_percent) - entry_mod:Save() - - if not config.copy_articulations and entry:GetArticulationFlag() then - for articulation in each(entry:CreateArticulations()) do - articulation:DeleteData() - end - entry:SetArticulationFlag(false) - end - if config.freeze_up_down > 0 then -- frozen stems requested - entry.FreezeStem = true - entry.StemUp = (config.freeze_up_down == 1) -- "true" -> upstem, "false" -> downstem - end - end - -- swap layer 1 with cuenote_layer & fix clef - layer.swap(destination_region, 1, config.cuenote_layer) - if not config.copy_clef then - clef.restore_default_clef(destination_region.StartMeasure, destination_region.EndMeasure, destination_staff) - end - - -- delete or amend text expressions - fix_text_expressions(destination_region) - -- check smart shapes - if not config.copy_smartshapes or not config.copy_slurs then - local marks = finale.FCSmartShapeMeasureMarks() - marks:LoadAllForRegion(destination_region, true) - for one_mark in each(marks) do - local shape = one_mark:CreateSmartShape() - if (shape:IsSlur() and not config.copy_slurs) or (not shape:IsSlur() and not config.copy_smartshapes) then - shape:DeleteData() - end - end - end - - -- create whole-note rest in layer 1 in each measure - for measure = destination_region.StartMeasure, destination_region.EndMeasure do - local notecell = finale.FCNoteEntryCell(measure, destination_staff) - notecell:Load() - local whole_note = notecell:AppendEntriesInLayer(1, 1) -- Append to layer 1, add 1 entry - if whole_note then - whole_note.Duration = finale.WHOLE_NOTE - whole_note.Legality = true - whole_note:MakeRest() - notecell:Save() - end - end - return true -end - -function new_expression_category(new_name) - local ok = false - local category_id = 0 - if not finenv.IsRGPLua then -- SaveNewWithType only works on RGPLua 0.58+ - return ok, category_id -- and crashes on JWLua - end - local new_category = finale.FCCategoryDef() - new_category:Load(finale.DEFAULTCATID_TECHNIQUETEXT) - local str = finale.FCString() - str.LuaString = new_name - new_category:SetName(str) - new_category:SetVerticalAlignmentPoint(finale.ALIGNVERT_STAFF_REFERENCE_LINE) - new_category:SetVerticalBaselineOffset(30) - new_category:SetHorizontalAlignmentPoint(finale.ALIGNHORIZ_CLICKPOS) - new_category:SetHorizontalOffset(-18) - -- make font slightly smaller than standard TECHNIQUE expression - local tfi = new_category:CreateTextFontInfo() - tfi.Size = tfi.Size - config.cue_font_smaller - new_category:SetTextFontInfo(tfi) - - ok = new_category:SaveNewWithType(finale.DEFAULTCATID_TECHNIQUETEXT) - if ok then - category_id = new_category:GetID() - end - return ok, category_id -end - -function assign_expression_to_staff(staff_number, measure_number, measure_position, expression_id) - local new_expression = finale.FCExpression() - new_expression:SetStaff(staff_number) - new_expression:SetVisible(true) - new_expression:SetMeasurePos(measure_position) - new_expression:SetScaleWithEntry(false) -- could possibly be true! - new_expression:SetPartAssignment(true) - new_expression:SetScoreAssignment(true) - new_expression:SetID(expression_id) - new_expression:SaveNewToCell( finale.FCCell(measure_number, staff_number) ) -end - -function create_cue_notes() - local cue_names = { } -- compile NAME/ItemNo of all pre-existing CUE_NAME expressions - local source_region = finenv.Region() - local start_staff = source_region.StartStaff - -- declare all other local variables - local ok, cd, expression_defs, cat_ID, expression_ID, name_index, destination_staff, new_expression - - if source_region:CalcStaffSpan() > 1 then - return show_error("only_one_staff") - elseif region_is_empty(source_region) then - return show_error("empty_region") - end - - cd = finale.FCCategoryDef() - expression_defs = finale.FCTextExpressionDefs() - expression_defs:LoadAll() - - -- collate extant cue names - for text_def in each(expression_defs) do - cd:Load(text_def.CategoryID) - if string.find(cd:CreateName().LuaString, config.cue_category_name) then - cat_ID = text_def.CategoryID - local str = text_def:CreateTextString() - str:TrimEnigmaTags() - -- save expresion NAME and ItemNo - table.insert(cue_names, {str.LuaString, text_def.ItemNo} ) - end - end - -- test for pre-existing names - if #cue_names == 0 then - -- create a new Text Expression Category - ok, cat_ID = new_expression_category(config.cue_category_name) - if not ok then -- creation failed - return show_error("first_make_expression_category") - end - end - -- choose cue name - ok, name_index = choose_name_index(cue_names) - if not ok then - return - end - if name_index == 0 then -- USER wants to provide a new cue name - ok, new_expression = new_cue_name(start_staff) - if not ok or new_expression == "" then - return - end - expression_ID = create_new_expression(new_expression, cat_ID) - else -- otherwise get the ItemNo of chosen pre-existing expression - expression_ID = cue_names[name_index][2] --([name_index][1] is the item name) - end - -- choose destination staff - ok, destination_staff = choose_destination_staff(start_staff) - if not ok then - return - end - -- save user preferences - save_config_file() -- save new config if any - -- make the cue copy - if not copy_to_destination(source_region, destination_staff) then - return - end - -- name the cue - assign_expression_to_staff(destination_staff, source_region.StartMeasure, 0, expression_ID) - -- reset visible selection to original staff - source_region:SetInDocument() -end - -create_cue_notes() -- go and do it already + ]]return"Cue Notes Create","Cue Notes Create","Copy as cue notes to another staff"end;local o=require("library.configuration")local p=require("library.clef")local q=require("library.layer")local r={copy_articulations=false,copy_expressions=false,copy_smartshapes=false,copy_slurs=true,copy_clef=false,mute_cuenotes=true,cuenote_percent=70,cuenote_layer=3,freeze_up_down=0,cue_category_name="Cue Names",cue_font_smaller=1,prefs_folder="script_settings",prefs_file="cue_notes_create.config"}o.get_parameters(r.prefs_file,r)function save_config_file()local s=finenv:RunningLuaFolderPath()..r.prefs_folder;local t=s..'/'..r.prefs_file;local u=io.open(t,"w")if nil==u then os.execute('mkdir "'..s..'"')u=io.open(t,"w")if nil==u then return end end;for v,w in pairs(r)do if type(w)=="boolean"then w=w and"true"or"false"elseif type(w)=="string"then w='"'..w..'"'end;u:write(v," = ",w,"\n")end;u:close()end;function show_error(x)local y={only_one_staff="Please select just one staff\n as the source for the new cue",empty_region="Please select a region\nwith some notes in it!",first_make_expression_category="You must first create a new Text Expression Category called \""..r.cue_category_name.."\" containing at least one entry"}finenv.UI():AlertNeutral("script: "..plugindef(),y[x])return-1 end;function should_overwrite_existing_music()local z=finenv.UI():AlertOkCancel("script: "..plugindef(),"Overwrite existing music?")local A=z==0;return A end;function region_is_empty(B)for C in eachentry(B)do if C.Count>0 then return false end end;return true end;function new_cue_name(D)local E=finale.FCCustomWindow()local F=finale.FCString()F.LuaString=plugindef()E:SetTitle(F)F.LuaString="New cue name:"E:CreateStatic(0,20):SetText(F)local G=E:CreateEdit(0,40)G:SetWidth(200)local H=finale.FCStaff()H:Load(D)G:SetText(H:CreateDisplayFullNameString())E:CreateOkButton()E:CreateCancelButton()local I=E:ExecuteModal(nil)==finale.EXECMODAL_OK;G:GetText(F)return I,F.LuaString end;function choose_name_index(J)local E=finale.FCCustomWindow()local F=finale.FCString()F.LuaString=plugindef()E:SetTitle(F)F.LuaString="Select cue name:"E:CreateStatic(0,20):SetText(F)local K=E:CreateListBox(0,40)K:SetWidth(200)F.LuaString="*** new name ***"K:AddString(F)for v,w in ipairs(J)do F.LuaString=w[1]K:AddString(F)end;E:CreateOkButton()E:CreateCancelButton()return E:ExecuteModal(nil)==finale.EXECMODAL_OK,K:GetSelectedItem()end;function create_new_expression(L,M)local N=finale.FCCategoryDef()N:Load(M)local O=N:CreateTextFontInfo()local F=finale.FCString()F.LuaString="^fontTxt"..O:CreateEnigmaString(finale.FCString()).LuaString..L;local P=finale.FCTextExpressionDef()P:SaveNewTextBlock(F)P:AssignToCategory(N)P:SetUseCategoryPos(true)P:SetUseCategoryFont(true)P:SaveNew()return P:GetItemNo()end;function choose_destination_staff(D)local K={}local Q=finenv.Region()local R=Q.StartSlot;Q:SetFullMeasureStack()local H=finale.FCStaff()for S=Q.StartSlot,Q.EndSlot do local T=Q:CalcStaffNumber(S)if T~=D then H:Load(T)table.insert(K,{T,H:CreateDisplayFullNameString().LuaString})end end;Q.StartSlot=R;Q.EndSlot=R;local U={210,310,360}local V=20;local W=finenv.UI():IsOnMac()and 3 or 0;local X={"copy_articulations","copy_expressions","copy_smartshapes","copy_slurs","copy_clef","mute_cuenotes","cuenote_percent","cuenote_layer","freeze_up_down"}local Y=6;local Z={}local F=finale.FCString()local E=finale.FCCustomLuaWindow()F.LuaString=plugindef()E:SetTitle(F)local _=E:CreateStatic(0,0)F.LuaString="Select destination staff:"_:SetText(F)_:SetWidth(200)local a0=E:CreateListBox(0,V)a0.UseCheckboxes=true;a0:SetWidth(200)for v,w in ipairs(K)do F.LuaString=w[2]a0:AddString(F)end;F.LuaString="Cue Options:"E:CreateStatic(U[1],0):SetText(F)for v,w in ipairs(X)do F.LuaString=string.gsub(w,'_',' ')if v<=Y then Z[v]=E:CreateCheckbox(U[1],v*V)Z[v]:SetText(F)Z[v]:SetWidth(120)local a1=r[w]and 1 or 0;Z[v]:SetCheck(a1)elseif v<#X then F.LuaString=F.LuaString..":"E:CreateStatic(U[1],v*V):SetText(F)Z[v]=E:CreateEdit(U[2],v*V-W)Z[v]:SetInteger(r[w])Z[v]:SetWidth(50)end end;local a2=E:CreatePopup(U[1],#X*V+5)F.LuaString="Stems: natural direction"a2:AddString(F)F.LuaString="Stems: freeze up"a2:AddString(F)F.LuaString="Stems: freeze down"a2:AddString(F)a2:SetWidth(160)a2:SetSelectedItem(r.freeze_up_down)local a3=E:CreateButton(U[3],V*2)F.LuaString="Clear All"a3:SetWidth(80)a3:SetText(F)E:RegisterHandleControlEvent(a3,function()for v=1,Y do Z[v]:SetCheck(0)end;a0:SetKeyboardFocus()end)local a4=E:CreateButton(U[3],V*4)F.LuaString="Set All"a4:SetWidth(80)a4:SetText(F)E:RegisterHandleControlEvent(a4,function()for v=1,Y do Z[v]:SetCheck(1)end;a0:SetKeyboardFocus()end)E:CreateOkButton()E:CreateCancelButton()local I=E:ExecuteModal(nil)==finale.EXECMODAL_OK;local a5=a0:GetSelectedItem()local a6=K[a5+1][1]for v,w in ipairs(X)do if v<=Y then r[w]=Z[v]:GetCheck()==1 elseif v<#X then local a7=Z[v]:GetInteger()if v==#Z and(a7<2 or a7>4)then a7=4 end;r[w]=a7 end end;r.freeze_up_down=a2:GetSelectedItem()return I,a6 end;function fix_text_expressions(B)local a8=finale.FCExpressions()a8:LoadAllForRegion(B)for a9 in eachbackwards(a8)do if a9.StaffGroupID==0 then if r.copy_expressions then a9.LayerAssignment=r.cuenote_layer;a9.ScaleWithEntry=true;a9:Save()else a9:DeleteData()end end end end;function copy_to_destination(aa,ab)local ac=finale.FCMusicRegion()ac:SetRegion(aa)ac:CopyMusic()ac.StartStaff=ab;ac.EndStaff=ab;if not region_is_empty(ac)and not should_overwrite_existing_music()then ac:ReleaseMusic()return false end;ac:PasteMusic()ac:ReleaseMusic()for ad=2,4 do q.clear(ac,ad)end;for C in eachentrysaved(ac)do if C:IsNote()and r.mute_cuenotes then C.Playback=false end;C:SetNoteDetailFlag(true)local ae=finale.FCEntryAlterMod()ae:SetNoteEntry(C)ae:SetResize(r.cuenote_percent)ae:Save()if not r.copy_articulations and C:GetArticulationFlag()then for af in each(C:CreateArticulations())do af:DeleteData()end;C:SetArticulationFlag(false)end;if r.freeze_up_down>0 then C.FreezeStem=true;C.StemUp=r.freeze_up_down==1 end end;q.swap(ac,1,r.cuenote_layer)if not r.copy_clef then p.restore_default_clef(ac.StartMeasure,ac.EndMeasure,ab)end;fix_text_expressions(ac)if not r.copy_smartshapes or not r.copy_slurs then local ag=finale.FCSmartShapeMeasureMarks()ag:LoadAllForRegion(ac,true)for ah in each(ag)do local ai=ah:CreateSmartShape()if ai:IsSlur()and not r.copy_slurs or not ai:IsSlur()and not r.copy_smartshapes then ai:DeleteData()end end end;for aj=ac.StartMeasure,ac.EndMeasure do local ak=finale.FCNoteEntryCell(aj,ab)ak:Load()local al=ak:AppendEntriesInLayer(1,1)if al then al.Duration=finale.WHOLE_NOTE;al.Legality=true;al:MakeRest()ak:Save()end end;return true end;function new_expression_category(am)local I=false;local an=0;if not finenv.IsRGPLua then return I,an end;local ao=finale.FCCategoryDef()ao:Load(finale.DEFAULTCATID_TECHNIQUETEXT)local F=finale.FCString()F.LuaString=am;ao:SetName(F)ao:SetVerticalAlignmentPoint(finale.ALIGNVERT_STAFF_REFERENCE_LINE)ao:SetVerticalBaselineOffset(30)ao:SetHorizontalAlignmentPoint(finale.ALIGNHORIZ_CLICKPOS)ao:SetHorizontalOffset(-18)local O=ao:CreateTextFontInfo()O.Size=O.Size-r.cue_font_smaller;ao:SetTextFontInfo(O)I=ao:SaveNewWithType(finale.DEFAULTCATID_TECHNIQUETEXT)if I then an=ao:GetID()end;return I,an end;function assign_expression_to_staff(T,ap,aq,ar)local as=finale.FCExpression()as:SetStaff(T)as:SetVisible(true)as:SetMeasurePos(aq)as:SetScaleWithEntry(false)as:SetPartAssignment(true)as:SetScoreAssignment(true)as:SetID(ar)as:SaveNewToCell(finale.FCCell(ap,T))end;function create_cue_notes()local at={}local aa=finenv.Region()local au=aa.StartStaff;local I,av,aw,ax,ay,az,ab,as;if aa:CalcStaffSpan()>1 then return show_error("only_one_staff")elseif region_is_empty(aa)then return show_error("empty_region")end;av=finale.FCCategoryDef()aw=finale.FCTextExpressionDefs()aw:LoadAll()for aA in each(aw)do av:Load(aA.CategoryID)if string.find(av:CreateName().LuaString,r.cue_category_name)then ax=aA.CategoryID;local F=aA:CreateTextString()F:TrimEnigmaTags()table.insert(at,{F.LuaString,aA.ItemNo})end end;if#at==0 then I,ax=new_expression_category(r.cue_category_name)if not I then return show_error("first_make_expression_category")end end;I,az=choose_name_index(at)if not I then return end;if az==0 then I,as=new_cue_name(au)if not I or as==""then return end;ay=create_new_expression(as,ax)else ay=at[az][2]end;I,ab=choose_destination_staff(au)if not I then return end;save_config_file()if not copy_to_destination(aa,ab)then return end;assign_expression_to_staff(ab,aa.StartMeasure,0,ay)aa:SetInDocument()end;create_cue_notes()end)c("library.layer",function(require,n,c,d)local aB={}function aB.finale_version(aC,aD,aE)local aF=bit32.bor(bit32.lshift(math.floor(aC),24),bit32.lshift(math.floor(aD),20))if aE then aF=bit32.bor(aF,math.floor(aE))end;return aF end;function aB.group_overlaps_region(aG,B)if B:IsFullDocumentSpan()then return true end;local aH=false;local aI=finale.FCSystemStaves()aI:LoadAllForRegion(B)for aJ in each(aI)do if aG:ContainsStaff(aJ:GetStaff())then aH=true;break end end;if not aH then return false end;if aG.StartMeasure>B.EndMeasure or aG.EndMeasure0 then aQ=true;break end;aO=aO+1 end;if aQ then local aR=finale.FCStaffSystem()aR:Load(aP:GetFirstSystem())return finale.FCCell(aR.FirstMeasure,aR.TopStaff)end;local aS=finale.FCMusicRegion()aS:SetFullDocument()return finale.FCCell(aS.EndMeasure,aS.EndStaff)end;function aB.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local aT=finale.FCMusicRegion()aT:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),aT.StartStaff)end;return aB.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function aB.get_top_left_selected_or_visible_cell()local aM=finenv.Region()if not aM:IsEmpty()then return finale.FCCell(aM.StartMeasure,aM.StartStaff)end;return aB.get_top_left_visible_cell()end;function aB.is_default_measure_number_visible_on_cell(aU,aV,aW,aX)local H=finale.FCCurrentStaffSpec()if not H:LoadForCell(aV,0)then return false end;if aU:GetShowOnTopStaff()and aV.Staff==aW.TopStaff then return true end;if aU:GetShowOnBottomStaff()and aV.Staff==aW:CalcBottomStaff()then return true end;if H.ShowMeasureNumbers then return not aU:GetExcludeOtherStaves(aX)end;return false end;function aB.is_default_number_visible_and_left_aligned(aU,aV,aY,aX,aZ)if aU.UseScoreInfoForParts then aX=false end;if aZ and aU:GetShowOnMultiMeasureRests(aX)then if finale.MNALIGN_LEFT~=aU:GetMultiMeasureAlignment(aX)then return false end elseif aV.Measure==aY.FirstMeasure then if not aU:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=aU:GetStartAlignment(aX)then return false end else if not aU:GetShowMultiples(aX)then return false end;if finale.MNALIGN_LEFT~=aU:GetMultipleAlignment(aX)then return false end end;return aB.is_default_measure_number_visible_on_cell(aU,aV,aY,aX)end;function aB.update_layout(a_,b0)a_=a_ or 1;b0=b0 or false;local b1=finale.FCPage()if b1:Load(a_)then b1:UpdateLayout(b0)end end;function aB.get_current_part()local b2=finale.FCParts()b2:LoadAll()return b2:GetCurrent()end;function aB.get_page_format_prefs()local b3=aB.get_current_part()local b4=finale.FCPageFormatPrefs()local b5=false;if b3:IsScore()then b5=b4:LoadScore()else b5=b4:LoadParts()end;return b4,b5 end;function aB.get_smufl_metadata_file(b6)if not b6 then b6=finale.FCFontInfo()b6:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local b7=function(b8,b6)local t=b8 .."/SMuFL/Fonts/"..b6.Name.."/"..b6.Name..".json"return io.open(t,"r")end;local b9=""if finenv.UI():IsOnWindows()then b9=os.getenv("LOCALAPPDATA")else b9=os.getenv("HOME").."/Library/Application Support"end;local ba=b7(b9,b6)if nil~=ba then return ba end;local bb="/Library/Application Support"if finenv.UI():IsOnWindows()then bb=os.getenv("COMMONPROGRAMFILES")end;return b7(bb,b6)end;function aB.is_font_smufl_font(b6)if not b6 then b6=finale.FCFontInfo()b6:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=aB.finale_version(27,1)then if nil~=b6.IsSMuFLFont then return b6.IsSMuFLFont end end;local bc=aB.get_smufl_metadata_file(b6)if nil~=bc then io.close(bc)return true end;return false end;function aB.simple_input(bd,be)local bf=finale.FCString()bf.LuaString=""local F=finale.FCString()local bg=160;function format_ctrl(bh,bi,bj,bk)bh:SetHeight(bi)bh:SetWidth(bj)F.LuaString=bk;bh:SetText(F)end;title_width=string.len(bd)*6+54;if title_width>bg then bg=title_width end;text_width=string.len(be)*6;if text_width>bg then bg=text_width end;F.LuaString=bd;local E=finale.FCCustomLuaWindow()E:SetTitle(F)local bl=E:CreateStatic(0,0)format_ctrl(bl,16,bg,be)local bm=E:CreateEdit(0,20)format_ctrl(bm,20,bg,"")E:CreateOkButton()E:CreateCancelButton()function callback(bh)end;E:RegisterHandleCommand(callback)if E:ExecuteModal(nil)==finale.EXECMODAL_OK then bf.LuaString=bm:GetText(bf)return bf.LuaString end end;function aB.is_finale_object(bn)return bn and type(bn)=="userdata"and bn.ClassName and bn.GetClassID and true or false end;function aB.system_indent_set_to_prefs(aY,b4)b4=b4 or aB.get_page_format_prefs()local bo=finale.FCMeasure()local bp=aY.FirstMeasure==1;if not bp and bo:Load(aY.FirstMeasure)then if bo.ShowFullNames then bp=true end end;if bp and b4.UseFirstSystemMargins then aY.LeftMargin=b4.FirstSystemLeft else aY.LeftMargin=b4.SystemLeft end;return aY:Save()end;return aB end)c("library.clef",function(require,n,c,d)local aB={}function aB.finale_version(aC,aD,aE)local aF=bit32.bor(bit32.lshift(math.floor(aC),24),bit32.lshift(math.floor(aD),20))if aE then aF=bit32.bor(aF,math.floor(aE))end;return aF end;function aB.group_overlaps_region(aG,B)if B:IsFullDocumentSpan()then return true end;local aH=false;local aI=finale.FCSystemStaves()aI:LoadAllForRegion(B)for aJ in each(aI)do if aG:ContainsStaff(aJ:GetStaff())then aH=true;break end end;if not aH then return false end;if aG.StartMeasure>B.EndMeasure or aG.EndMeasure0 then aQ=true;break end;aO=aO+1 end;if aQ then local aR=finale.FCStaffSystem()aR:Load(aP:GetFirstSystem())return finale.FCCell(aR.FirstMeasure,aR.TopStaff)end;local aS=finale.FCMusicRegion()aS:SetFullDocument()return finale.FCCell(aS.EndMeasure,aS.EndStaff)end;function aB.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local aT=finale.FCMusicRegion()aT:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),aT.StartStaff)end;return aB.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function aB.get_top_left_selected_or_visible_cell()local aM=finenv.Region()if not aM:IsEmpty()then return finale.FCCell(aM.StartMeasure,aM.StartStaff)end;return aB.get_top_left_visible_cell()end;function aB.is_default_measure_number_visible_on_cell(aU,aV,aW,aX)local H=finale.FCCurrentStaffSpec()if not H:LoadForCell(aV,0)then return false end;if aU:GetShowOnTopStaff()and aV.Staff==aW.TopStaff then return true end;if aU:GetShowOnBottomStaff()and aV.Staff==aW:CalcBottomStaff()then return true end;if H.ShowMeasureNumbers then return not aU:GetExcludeOtherStaves(aX)end;return false end;function aB.is_default_number_visible_and_left_aligned(aU,aV,aY,aX,aZ)if aU.UseScoreInfoForParts then aX=false end;if aZ and aU:GetShowOnMultiMeasureRests(aX)then if finale.MNALIGN_LEFT~=aU:GetMultiMeasureAlignment(aX)then return false end elseif aV.Measure==aY.FirstMeasure then if not aU:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=aU:GetStartAlignment(aX)then return false end else if not aU:GetShowMultiples(aX)then return false end;if finale.MNALIGN_LEFT~=aU:GetMultipleAlignment(aX)then return false end end;return aB.is_default_measure_number_visible_on_cell(aU,aV,aY,aX)end;function aB.update_layout(a_,b0)a_=a_ or 1;b0=b0 or false;local b1=finale.FCPage()if b1:Load(a_)then b1:UpdateLayout(b0)end end;function aB.get_current_part()local b2=finale.FCParts()b2:LoadAll()return b2:GetCurrent()end;function aB.get_page_format_prefs()local b3=aB.get_current_part()local b4=finale.FCPageFormatPrefs()local b5=false;if b3:IsScore()then b5=b4:LoadScore()else b5=b4:LoadParts()end;return b4,b5 end;function aB.get_smufl_metadata_file(b6)if not b6 then b6=finale.FCFontInfo()b6:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local b7=function(b8,b6)local t=b8 .."/SMuFL/Fonts/"..b6.Name.."/"..b6.Name..".json"return io.open(t,"r")end;local b9=""if finenv.UI():IsOnWindows()then b9=os.getenv("LOCALAPPDATA")else b9=os.getenv("HOME").."/Library/Application Support"end;local ba=b7(b9,b6)if nil~=ba then return ba end;local bb="/Library/Application Support"if finenv.UI():IsOnWindows()then bb=os.getenv("COMMONPROGRAMFILES")end;return b7(bb,b6)end;function aB.is_font_smufl_font(b6)if not b6 then b6=finale.FCFontInfo()b6:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=aB.finale_version(27,1)then if nil~=b6.IsSMuFLFont then return b6.IsSMuFLFont end end;local bc=aB.get_smufl_metadata_file(b6)if nil~=bc then io.close(bc)return true end;return false end;function aB.simple_input(bd,be)local bf=finale.FCString()bf.LuaString=""local F=finale.FCString()local bg=160;function format_ctrl(bh,bi,bj,bk)bh:SetHeight(bi)bh:SetWidth(bj)F.LuaString=bk;bh:SetText(F)end;title_width=string.len(bd)*6+54;if title_width>bg then bg=title_width end;text_width=string.len(be)*6;if text_width>bg then bg=text_width end;F.LuaString=bd;local E=finale.FCCustomLuaWindow()E:SetTitle(F)local bl=E:CreateStatic(0,0)format_ctrl(bl,16,bg,be)local bm=E:CreateEdit(0,20)format_ctrl(bm,20,bg,"")E:CreateOkButton()E:CreateCancelButton()function callback(bh)end;E:RegisterHandleCommand(callback)if E:ExecuteModal(nil)==finale.EXECMODAL_OK then bf.LuaString=bm:GetText(bf)return bf.LuaString end end;function aB.is_finale_object(bn)return bn and type(bn)=="userdata"and bn.ClassName and bn.GetClassID and true or false end;function aB.system_indent_set_to_prefs(aY,b4)b4=b4 or aB.get_page_format_prefs()local bo=finale.FCMeasure()local bp=aY.FirstMeasure==1;if not bp and bo:Load(aY.FirstMeasure)then if bo.ShowFullNames then bp=true end end;if bp and b4.UseFirstSystemMargins then aY.LeftMargin=b4.FirstSystemLeft else aY.LeftMargin=b4.SystemLeft end;return aY:Save()end;return aB end)c("library.configuration",function(require,n,c,d)local aB={}function aB.finale_version(aC,aD,aE)local aF=bit32.bor(bit32.lshift(math.floor(aC),24),bit32.lshift(math.floor(aD),20))if aE then aF=bit32.bor(aF,math.floor(aE))end;return aF end;function aB.group_overlaps_region(aG,B)if B:IsFullDocumentSpan()then return true end;local aH=false;local aI=finale.FCSystemStaves()aI:LoadAllForRegion(B)for aJ in each(aI)do if aG:ContainsStaff(aJ:GetStaff())then aH=true;break end end;if not aH then return false end;if aG.StartMeasure>B.EndMeasure or aG.EndMeasure0 then aQ=true;break end;aO=aO+1 end;if aQ then local aR=finale.FCStaffSystem()aR:Load(aP:GetFirstSystem())return finale.FCCell(aR.FirstMeasure,aR.TopStaff)end;local aS=finale.FCMusicRegion()aS:SetFullDocument()return finale.FCCell(aS.EndMeasure,aS.EndStaff)end;function aB.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local aT=finale.FCMusicRegion()aT:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),aT.StartStaff)end;return aB.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function aB.get_top_left_selected_or_visible_cell()local aM=finenv.Region()if not aM:IsEmpty()then return finale.FCCell(aM.StartMeasure,aM.StartStaff)end;return aB.get_top_left_visible_cell()end;function aB.is_default_measure_number_visible_on_cell(aU,aV,aW,aX)local H=finale.FCCurrentStaffSpec()if not H:LoadForCell(aV,0)then return false end;if aU:GetShowOnTopStaff()and aV.Staff==aW.TopStaff then return true end;if aU:GetShowOnBottomStaff()and aV.Staff==aW:CalcBottomStaff()then return true end;if H.ShowMeasureNumbers then return not aU:GetExcludeOtherStaves(aX)end;return false end;function aB.is_default_number_visible_and_left_aligned(aU,aV,aY,aX,aZ)if aU.UseScoreInfoForParts then aX=false end;if aZ and aU:GetShowOnMultiMeasureRests(aX)then if finale.MNALIGN_LEFT~=aU:GetMultiMeasureAlignment(aX)then return false end elseif aV.Measure==aY.FirstMeasure then if not aU:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=aU:GetStartAlignment(aX)then return false end else if not aU:GetShowMultiples(aX)then return false end;if finale.MNALIGN_LEFT~=aU:GetMultipleAlignment(aX)then return false end end;return aB.is_default_measure_number_visible_on_cell(aU,aV,aY,aX)end;function aB.update_layout(a_,b0)a_=a_ or 1;b0=b0 or false;local b1=finale.FCPage()if b1:Load(a_)then b1:UpdateLayout(b0)end end;function aB.get_current_part()local b2=finale.FCParts()b2:LoadAll()return b2:GetCurrent()end;function aB.get_page_format_prefs()local b3=aB.get_current_part()local b4=finale.FCPageFormatPrefs()local b5=false;if b3:IsScore()then b5=b4:LoadScore()else b5=b4:LoadParts()end;return b4,b5 end;function aB.get_smufl_metadata_file(b6)if not b6 then b6=finale.FCFontInfo()b6:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local b7=function(b8,b6)local t=b8 .."/SMuFL/Fonts/"..b6.Name.."/"..b6.Name..".json"return io.open(t,"r")end;local b9=""if finenv.UI():IsOnWindows()then b9=os.getenv("LOCALAPPDATA")else b9=os.getenv("HOME").."/Library/Application Support"end;local ba=b7(b9,b6)if nil~=ba then return ba end;local bb="/Library/Application Support"if finenv.UI():IsOnWindows()then bb=os.getenv("COMMONPROGRAMFILES")end;return b7(bb,b6)end;function aB.is_font_smufl_font(b6)if not b6 then b6=finale.FCFontInfo()b6:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=aB.finale_version(27,1)then if nil~=b6.IsSMuFLFont then return b6.IsSMuFLFont end end;local bc=aB.get_smufl_metadata_file(b6)if nil~=bc then io.close(bc)return true end;return false end;function aB.simple_input(bd,be)local bf=finale.FCString()bf.LuaString=""local F=finale.FCString()local bg=160;function format_ctrl(bh,bi,bj,bk)bh:SetHeight(bi)bh:SetWidth(bj)F.LuaString=bk;bh:SetText(F)end;title_width=string.len(bd)*6+54;if title_width>bg then bg=title_width end;text_width=string.len(be)*6;if text_width>bg then bg=text_width end;F.LuaString=bd;local E=finale.FCCustomLuaWindow()E:SetTitle(F)local bl=E:CreateStatic(0,0)format_ctrl(bl,16,bg,be)local bm=E:CreateEdit(0,20)format_ctrl(bm,20,bg,"")E:CreateOkButton()E:CreateCancelButton()function callback(bh)end;E:RegisterHandleCommand(callback)if E:ExecuteModal(nil)==finale.EXECMODAL_OK then bf.LuaString=bm:GetText(bf)return bf.LuaString end end;function aB.is_finale_object(bn)return bn and type(bn)=="userdata"and bn.ClassName and bn.GetClassID and true or false end;function aB.system_indent_set_to_prefs(aY,b4)b4=b4 or aB.get_page_format_prefs()local bo=finale.FCMeasure()local bp=aY.FirstMeasure==1;if not bp and bo:Load(aY.FirstMeasure)then if bo.ShowFullNames then bp=true end end;if bp and b4.UseFirstSystemMargins then aY.LeftMargin=b4.FirstSystemLeft else aY.LeftMargin=b4.SystemLeft end;return aY:Save()end;return aB end)return a("__root") \ No newline at end of file diff --git a/dist/delete_selective.lua b/dist/delete_selective.lua index ca85035d..fc0b4165 100644 --- a/dist/delete_selective.lua +++ b/dist/delete_selective.lua @@ -1,11 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Carl Vine" - finaleplugin.AuthorURL = "http://carlvine.com/lua" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "0.51" - finaleplugin.Date = "2022/06/24" - finaleplugin.AdditionalMenuOptions = [[ +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Carl Vine"finaleplugin.AuthorURL="http://carlvine.com/lua"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="0.51"finaleplugin.Date="2022/06/24"finaleplugin.AdditionalMenuOptions=[[ Delete dynamics Delete expressions (not dynamics) Delete expressions (measure-attached) @@ -16,8 +9,7 @@ function plugindef() Delete glissandos Delete smart shapes (beat aligned) Delete all smart shapes - ]] - finaleplugin.AdditionalUndoText = [[ + ]]finaleplugin.AdditionalUndoText=[[ Delete dynamics Delete expressions (not dynamics) Delete expressions (measure-attached) @@ -28,8 +20,7 @@ function plugindef() Delete glissandos Delete smart shapes (beat aligned) Delete all smart shapes - ]] - finaleplugin.AdditionalDescriptions = [[ + ]]finaleplugin.AdditionalDescriptions=[[ Delete dynamics from the selected region Delete expressions (not dynamics) from the selected region Delete measure-assigned expressions from the selected region @@ -40,8 +31,7 @@ function plugindef() Delete glissandos from the selected region Delete smart shapes (beat aligned) from the selected region Delete all smart shapes from the selected region - ]] - finaleplugin.AdditionalPrefixes = [[ + ]]finaleplugin.AdditionalPrefixes=[[ delete_type = "expression_dynamic" delete_type = "expression_not_dynamic" delete_type = "measure_attached" @@ -52,75 +42,11 @@ function plugindef() delete_type = "shape_glissando" delete_type = "shape_beat_aligned" delete_type = "shape_all" - ]] - finaleplugin.Notes = [[ + ]]finaleplugin.Notes=[[ Deletes nominated items from the selected region, defaulting to a primary menu item: "Delete all expressions". Under RGPLua (0.62+) nine additional menu items are created to independently delete other items of these types: dynamics / expressions (not dynamics) / expressions (measure-attached) / articulations / hairpins / slurs / custom lines / glissandos / smart shapes (beat aligned) / all smart shapes - ]] - return "Delete all expressions", "Delete all expressions", "Delete all expressions from the selected region" -end - -delete_type = delete_type or "expression_all" - -function delete_selected() - if string.find(delete_type, "shape") then -- SMART SHAPE - local marks = finale.FCSmartShapeMeasureMarks() - marks:LoadAllForRegion(finenv.Region(), true) - for mark in each(marks) do - local shape = mark:CreateSmartShape() - if (delete_type == "shape_hairpin" and shape:IsHairpin()) - or (delete_type == "shape_slur" and shape:IsSlur()) - or (delete_type == "shape_custom" and shape:IsCustomLine()) - or (delete_type == "shape_glissando" and shape:IsGlissando()) - or (delete_type == "shape_beat_aligned" and not shape:IsEntryBased()) - or (delete_type == "shape_all") - then - shape:DeleteData() - end - end - elseif string.find(delete_type, "express") then -- EXPRESSION type - local expressions = finale.FCExpressions() - expressions:LoadAllForRegion(finenv.Region()) - for exp in eachbackwards(expressions) do - local def_id = exp:CreateTextExpressionDef().CategoryID -- test for DYNAMICS - if not exp:IsShape() and exp.StaffGroupID == 0 and - ( (delete_type == "expression_all") - or (delete_type == "expression_not_dynamic" and def_id ~= finale.DEFAULTCATID_DYNAMICS) - or (delete_type == "expression_dynamic" and def_id == finale.DEFAULTCATID_DYNAMICS) - ) - then - exp:DeleteData() - end - end - elseif delete_type == "measure_attached" then -- MEASURE-ATTACHED EXPRESSIONS type - local measures = finale.FCMeasures() - measures:LoadRegion(finenv.Region()) - local try = finale.FCExpression() - for measure in each(measures) do - for exp in eachbackwards(measure:CreateExpressions()) do - if exp.StaffGroupID > 0 then - exp:DeleteData() - end - end - if not try:Load(measure.ItemNo, 0) then - measure.ExpressionFlag = false -- no expressions left - measure:Save() - end - end - elseif delete_type == "articulation" then -- ARTICULATION type - for entry in eachentrysaved(finenv.Region()) do - if entry:GetArticulationFlag() then - for articulation in eachbackwards(entry:CreateArticulations()) do - articulation:DeleteData() - end - entry:SetArticulationFlag(false) - end - end - end -end - -delete_selected() + ]]return"Delete all expressions","Delete all expressions","Delete all expressions from the selected region"end;delete_type=delete_type or"expression_all"function delete_selected()if string.find(delete_type,"shape")then local a=finale.FCSmartShapeMeasureMarks()a:LoadAllForRegion(finenv.Region(),true)for b in each(a)do local c=b:CreateSmartShape()if delete_type=="shape_hairpin"and c:IsHairpin()or delete_type=="shape_slur"and c:IsSlur()or delete_type=="shape_custom"and c:IsCustomLine()or delete_type=="shape_glissando"and c:IsGlissando()or delete_type=="shape_beat_aligned"and not c:IsEntryBased()or delete_type=="shape_all"then c:DeleteData()end end elseif string.find(delete_type,"express")then local d=finale.FCExpressions()d:LoadAllForRegion(finenv.Region())for e in eachbackwards(d)do local f=e:CreateTextExpressionDef().CategoryID;if not e:IsShape()and e.StaffGroupID==0 and(delete_type=="expression_all"or delete_type=="expression_not_dynamic"and f~=finale.DEFAULTCATID_DYNAMICS or delete_type=="expression_dynamic"and f==finale.DEFAULTCATID_DYNAMICS)then e:DeleteData()end end elseif delete_type=="measure_attached"then local g=finale.FCMeasures()g:LoadRegion(finenv.Region())local h=finale.FCExpression()for i in each(g)do for e in eachbackwards(i:CreateExpressions())do if e.StaffGroupID>0 then e:DeleteData()end end;if not h:Load(i.ItemNo,0)then i.ExpressionFlag=false;i:Save()end end elseif delete_type=="articulation"then for j in eachentrysaved(finenv.Region())do if j:GetArticulationFlag()then for k in eachbackwards(j:CreateArticulations())do k:DeleteData()end;j:SetArticulationFlag(false)end end end end;delete_selected() \ No newline at end of file diff --git a/dist/dynamics_move_above_staff.lua b/dist/dynamics_move_above_staff.lua index 9b3fbc26..c2175678 100644 --- a/dist/dynamics_move_above_staff.lua +++ b/dist/dynamics_move_above_staff.lua @@ -1,162 +1 @@ -function plugindef() - -- This function and the 'finaleplugin' namespace - -- are both reserved for the plug-in definition. - finaleplugin.RequireScore = false - finaleplugin.RequireSelection = true - finaleplugin.Author = "Jacob Winkler" - finaleplugin.Copyright = "2021" - finaleplugin.Version = "1.0" - return "Dynamics Above Staff", "Dynamics Above Staff", "Moves dynamics above staff" -end - - -function dyn_above() - local region = finenv.Region() - local start_msr = region.StartMeasure - local end_msr = region.EndMeasure - local start_staff = region.StartStaff - local end_staff = region.EndStaff - local measures = finale.FCMeasures() - measures:LoadRegion(region) - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - local staffsystems = finale.FCStaffSystems() - staffsystems:LoadAll() - local staffsys = finale.FCStaffSystem() - local start_staffsys = staffsystems:FindMeasureNumber(start_msr) - local end_staffsys = staffsystems:FindMeasureNumber(end_msr) - print("Start Staffsys is",start_staffsys.ItemNo,", End is",end_staffsys.ItemNo) - local baseline = finale.FCBaseline() - baseline.Mode = finale.BASELINEMODE_EXPRESSIONABOVE - baseline:LoadDefaultForMode(finale.BASELINEMODE_EXPRESSIONABOVE) - baseline_off = baseline.VerticalOffset - print("Above Staff Baseline is",baseline_off) - local move_by = -84 - local e_vert_target = 0 - local h_vert_target = 0 - local sys_ref_line = 0 - local vocal_dynamic_offset = 36 -- Distance above reference line to place dynamics, in absence of other entries - -function metrics(sys_region) - print("metrics function called") - local highest = 0 - local hairpins = 0 - measures:LoadRegion(sys_region) - for msr in each(measures) do - print("Analyzing measure",msr.ItemNo) - cell = finale.FCCell(msr.ItemNo, sys_region.StartStaff) - cellmetrics = cell:CreateCellMetrics() - --**** May need to account for cellmetrics:GetStaffScaling()... - staff_scale = cellmetrics:GetStaffScaling() / 10000 - if cellmetrics.ReferenceLinePos + vocal_dynamic_offset > highest then - highest = cellmetrics.ReferenceLinePos + vocal_dynamic_offset - end - end -- for msr.. - for entry in eachentry(sys_region) do - local e_metrics = finale.FCEntryMetrics() - e_metrics:Load(entry) - local e_highest = e_metrics:GetTopPosition() / staff_scale - if e_highest + vocal_dynamic_offset > highest then - highest = e_highest + vocal_dynamic_offset - end - end - hairpins = highest - cellmetrics.ReferenceLinePos +12 - - return highest, hairpins -end -- function metrics - -function expr_move(staff_region, e_vert_target) - local expressions = finale.FCExpressions() - expressions:LoadAllForRegion(staff_region) - for e in each(expressions) do - local dynamic = false - local sed = e:CreateTextExpressionDef() - local cat_ID = sed:GetCategoryID() - local cd = finale.FCCategoryDef() - if cd:Load(cat_ID) then - local cat_name = cd:CreateName() - --print(cat_name.LuaString) - if cat_name.LuaString == "Dynamics" then - dynamic = true - end - end - if dynamic == true then - print("VerticalPos",e.VerticalPos) - local e_metric = finale.FCPoint(0, 0) - cell = finale.FCCell(e.Measure, e.Staff) - cellmetrics = cell:CreateCellMetrics() - ---- CHANGE ME! - --e_vert_target = cellmetrics.ReferenceLinePos + baseline_off + move_by - 12 -- vert_target could be calculated somehwere else... - ---- - print("Vertical Target is",e_vert_target) - e:CalcMetricPos(e_metric) - print("Expression Y is",e_metric.Y) - e:SetVerticalPos(e.VerticalPos + (e_vert_target - e_metric.Y)) - e:Save() - end -- if dynamic == true - end -- for e... -end -- func expr_move - - -function hairpin_move(staff_region, h_vert_target) - local ssmm = finale.FCSmartShapeMeasureMarks() - ssmm:LoadAllForRegion(staff_region, true) - for mark in each(ssmm) do - local smart_shape = mark:CreateSmartShape() - if smart_shape:IsHairpin() then - print("found hairpin") - local left_seg = smart_shape:GetTerminateSegmentLeft() - local right_seg = smart_shape:GetTerminateSegmentRight() --- left_seg:SetEndpointOffsetY(baseline_off + move_by) - -- right_seg:SetEndpointOffsetY(baseline_off + move_by) - left_seg:SetEndpointOffsetY(h_vert_target) - right_seg:SetEndpointOffsetY(h_vert_target) - - smart_shape:Save() - - end - end -end -- func hairpin_move - -function analyze_staves() - for i = start_staffsys.ItemNo, end_staffsys.ItemNo, 1 do - print("Analyzing staffsys",i) - staffsys:Load(i) - local sys_region_start = 0 - local sys_region_end = 0 - if start_msr > staffsys.FirstMeasure then - sys_region_start = start_msr - else - sys_region_start = staffsys.FirstMeasure - end - if end_msr < (staffsys.NextSysMeasure - 1) then - sys_region_end = end_msr - else - sys_region_end = staffsys.NextSysMeasure - 1 - end - print("Start Measure",sys_region_start, "End", sys_region_end) - local sys_region = finenv.Region() - sys_region:SetStartMeasure(sys_region_start) - sys_region:SetEndMeasure(sys_region_end) - for j = start_staff, end_staff, 1 do - sys_region:SetStartStaff(j) - sys_region:SetEndStaff(j) - e_vert_target, h_vert_target = metrics(sys_region) - print("vert_target for staff",j,"is",e_vert_target) - expr_move(sys_region, e_vert_target) - hairpin_move(sys_region, h_vert_target) - end -- for j = start_staff... - - end -- for i... -end -- function - -analyze_staves() - ---[[ - expr_move() - hairpin_move() -]] - -end -- function - -dyn_above() +function plugindef()finaleplugin.RequireScore=false;finaleplugin.RequireSelection=true;finaleplugin.Author="Jacob Winkler"finaleplugin.Copyright="2021"finaleplugin.Version="1.0"return"Dynamics Above Staff","Dynamics Above Staff","Moves dynamics above staff"end;function dyn_above()local a=finenv.Region()local b=a.StartMeasure;local c=a.EndMeasure;local d=a.StartStaff;local e=a.EndStaff;local f=finale.FCMeasures()f:LoadRegion(a)local g=finale.FCSystemStaves()g:LoadAllForRegion(a)local h=finale.FCStaffSystems()h:LoadAll()local i=finale.FCStaffSystem()local j=h:FindMeasureNumber(b)local k=h:FindMeasureNumber(c)print("Start Staffsys is",j.ItemNo,", End is",k.ItemNo)local l=finale.FCBaseline()l.Mode=finale.BASELINEMODE_EXPRESSIONABOVE;l:LoadDefaultForMode(finale.BASELINEMODE_EXPRESSIONABOVE)baseline_off=l.VerticalOffset;print("Above Staff Baseline is",baseline_off)local m=-84;local n=0;local o=0;local p=0;local q=36;function metrics(r)print("metrics function called")local s=0;local t=0;f:LoadRegion(r)for u in each(f)do print("Analyzing measure",u.ItemNo)cell=finale.FCCell(u.ItemNo,r.StartStaff)cellmetrics=cell:CreateCellMetrics()staff_scale=cellmetrics:GetStaffScaling()/10000;if cellmetrics.ReferenceLinePos+q>s then s=cellmetrics.ReferenceLinePos+q end end;for v in eachentry(r)do local w=finale.FCEntryMetrics()w:Load(v)local x=w:GetTopPosition()/staff_scale;if x+q>s then s=x+q end end;t=s-cellmetrics.ReferenceLinePos+12;return s,t end;function expr_move(y,n)local z=finale.FCExpressions()z:LoadAllForRegion(y)for A in each(z)do local B=false;local C=A:CreateTextExpressionDef()local D=C:GetCategoryID()local E=finale.FCCategoryDef()if E:Load(D)then local F=E:CreateName()if F.LuaString=="Dynamics"then B=true end end;if B==true then print("VerticalPos",A.VerticalPos)local G=finale.FCPoint(0,0)cell=finale.FCCell(A.Measure,A.Staff)cellmetrics=cell:CreateCellMetrics()print("Vertical Target is",n)A:CalcMetricPos(G)print("Expression Y is",G.Y)A:SetVerticalPos(A.VerticalPos+n-G.Y)A:Save()end end end;function hairpin_move(y,o)local H=finale.FCSmartShapeMeasureMarks()H:LoadAllForRegion(y,true)for I in each(H)do local J=I:CreateSmartShape()if J:IsHairpin()then print("found hairpin")local K=J:GetTerminateSegmentLeft()local L=J:GetTerminateSegmentRight()K:SetEndpointOffsetY(o)L:SetEndpointOffsetY(o)J:Save()end end end;function analyze_staves()for M=j.ItemNo,k.ItemNo,1 do print("Analyzing staffsys",M)i:Load(M)local N=0;local O=0;if b>i.FirstMeasure then N=b else N=i.FirstMeasure end;if c 288 (12 spaces is 288 EVPUs) -- `8.5i` => 2448 (8.5 inches is 2448 EVPUs) -- `10cm` => 1133 (10 centimeters is 1133 EVPUs) -- `10mm` => 113 (10 millimeters is 113 EVPUs) -- `1pt` => 4 (1 point is 4 EVPUs) -- `2.5p` => 120 (2.5 picas is 120 EVPUs) - -Read the [Finale User Manual](https://usermanuals.finalemusic.com/FinaleMac/Content/Finale/def-equivalents.htm#overriding-global-measurement-units) -for more details about measurement units in Finale. - -@ text (string) the string to convert -: (number) the converted number of EVPUs -]] -function measurement.convert_to_EVPUs(text) - local str = finale.FCString() - str.LuaString = text - return str:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) -end - ---[[ -% get_unit_name - -Returns the name of a measurement unit. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_name(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_names[unit] -end - ---[[ -% get_unit_suffix - -Returns the measurement unit's suffix. Suffixes can be used to force the text value (eg in `FCString` or `FCCtrlEdit`) to be treated as being from a particular measurement unit -Note that although this method returns a "p" for Picas, the fractional part goes after the "p" (eg `1p6`), so in practice it may be that no suffix is needed. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_suffix(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_suffixes[unit] -end - ---[[ -% get_unit_abbreviation - -Returns measurement unit abbreviations that are more human-readable than Finale's internal suffixes. -Abbreviations are also compatible with the internal ones because Finale discards everything after the first letter that isn't part of the suffix. - -For example: -```lua -local str_internal = finale.FCString() -str.LuaString = "2i" - -local str_display = finale.FCString() -str.LuaString = "2in" - -print(str_internal:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) == str_display:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT)) -- true -``` - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_abbreviation(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_abbreviations[unit] -end - ---[[ -% is_valid_unit - -Checks if a number is equal to one of the finale MEASUREMENTUNIT constants. - -@ unit (number) The unit to check. -: (boolean) `true` if valid, `false` if not. -]] -function measurement.is_valid_unit(unit) - return unit_names[unit] and true or false -end - ---[[ -% get_real_default_unit - -Resolves `finale.MEASUREMENTUNIT_DEFAULT` to the value of one of the other `MEASUREMENTUNIT` constants. - -: (number) -]] -function measurement.get_real_default_unit() - local str = finale.FCString() - finenv.UI():GetDecimalSeparator(str) - local separator = str.LuaString - str:SetMeasurement(72, finale.MEASUREMENTUNIT_DEFAULT) - - if str.LuaString == "72" then - return finale.MEASUREMENTUNIT_EVPUS - elseif str.LuaString == "0" .. separator .. "25" then - return finale.MEASUREMENTUNIT_INCHES - elseif str.LuaString == "0" .. separator .. "635" then - return finale.MEASUREMENTUNIT_CENTIMETERS - elseif str.LuaString == "18" then - return finale.MEASUREMENTUNIT_POINTS - elseif str.LuaString == "1p6" then - return finale.MEASUREMENTUNIT_PICAS - elseif str.LuaString == "3" then - return finale.MEASUREMENTUNIT_SPACES - end -end - - - - -function expression_baseline_below_move_down() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - baselines:LoadAllForSystem(finale.BASELINEMODE_EXPRESSIONBELOW, i) - for j = start_slot, end_slot do - bl = baselines:AssureSavedStaff(finale.BASELINEMODE_EXPRESSIONBELOW, i, region:CalcStaffNumber(j)) - bl.VerticalOffset = bl.VerticalOffset - measurement.convert_to_EVPUs("1s") - bl:Save() - end - end -end - -expression_baseline_below_move_down() diff --git a/dist/expression_baseline_below_move_up.lua b/dist/expression_baseline_below_move_up.lua deleted file mode 100644 index 20d94a89..00000000 --- a/dist/expression_baseline_below_move_up.lua +++ /dev/null @@ -1,199 +0,0 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Version = "1.0" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "July 7, 2021" - finaleplugin.CategoryTags = "Expression" - finaleplugin.AuthorURL = "http://robertgpatterson.com" - return "Move Expression Baseline Below Up", "Move Expression Baseline Below Up", - "Moves the selected expression below baseline up one space" -end - ---[[ -$module measurement -]] -- -local measurement = {} - -local unit_names = { - [finale.MEASUREMENTUNIT_EVPUS] = "EVPUs", - [finale.MEASUREMENTUNIT_INCHES] = "Inches", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "Centimeters", - [finale.MEASUREMENTUNIT_POINTS] = "Points", - [finale.MEASUREMENTUNIT_PICAS] = "Picas", - [finale.MEASUREMENTUNIT_SPACES] = "Spaces", -} - -local unit_suffixes = { - [finale.MEASUREMENTUNIT_EVPUS] = "e", - [finale.MEASUREMENTUNIT_INCHES] = "i", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "c", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "p", - [finale.MEASUREMENTUNIT_SPACES] = "s", -} - -local unit_abbreviations = { - [finale.MEASUREMENTUNIT_EVPUS] = "ev", - [finale.MEASUREMENTUNIT_INCHES] = "in", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "cm", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "pc", - [finale.MEASUREMENTUNIT_SPACES] = "sp", -} - ---[[ -% convert_to_EVPUs - -Converts the specified string into EVPUs. Like text boxes in Finale, this supports -the usage of units at the end of the string. The following are a few examples: - -- `12s` => 288 (12 spaces is 288 EVPUs) -- `8.5i` => 2448 (8.5 inches is 2448 EVPUs) -- `10cm` => 1133 (10 centimeters is 1133 EVPUs) -- `10mm` => 113 (10 millimeters is 113 EVPUs) -- `1pt` => 4 (1 point is 4 EVPUs) -- `2.5p` => 120 (2.5 picas is 120 EVPUs) - -Read the [Finale User Manual](https://usermanuals.finalemusic.com/FinaleMac/Content/Finale/def-equivalents.htm#overriding-global-measurement-units) -for more details about measurement units in Finale. - -@ text (string) the string to convert -: (number) the converted number of EVPUs -]] -function measurement.convert_to_EVPUs(text) - local str = finale.FCString() - str.LuaString = text - return str:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) -end - ---[[ -% get_unit_name - -Returns the name of a measurement unit. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_name(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_names[unit] -end - ---[[ -% get_unit_suffix - -Returns the measurement unit's suffix. Suffixes can be used to force the text value (eg in `FCString` or `FCCtrlEdit`) to be treated as being from a particular measurement unit -Note that although this method returns a "p" for Picas, the fractional part goes after the "p" (eg `1p6`), so in practice it may be that no suffix is needed. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_suffix(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_suffixes[unit] -end - ---[[ -% get_unit_abbreviation - -Returns measurement unit abbreviations that are more human-readable than Finale's internal suffixes. -Abbreviations are also compatible with the internal ones because Finale discards everything after the first letter that isn't part of the suffix. - -For example: -```lua -local str_internal = finale.FCString() -str.LuaString = "2i" - -local str_display = finale.FCString() -str.LuaString = "2in" - -print(str_internal:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) == str_display:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT)) -- true -``` - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_abbreviation(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_abbreviations[unit] -end - ---[[ -% is_valid_unit - -Checks if a number is equal to one of the finale MEASUREMENTUNIT constants. - -@ unit (number) The unit to check. -: (boolean) `true` if valid, `false` if not. -]] -function measurement.is_valid_unit(unit) - return unit_names[unit] and true or false -end - ---[[ -% get_real_default_unit - -Resolves `finale.MEASUREMENTUNIT_DEFAULT` to the value of one of the other `MEASUREMENTUNIT` constants. - -: (number) -]] -function measurement.get_real_default_unit() - local str = finale.FCString() - finenv.UI():GetDecimalSeparator(str) - local separator = str.LuaString - str:SetMeasurement(72, finale.MEASUREMENTUNIT_DEFAULT) - - if str.LuaString == "72" then - return finale.MEASUREMENTUNIT_EVPUS - elseif str.LuaString == "0" .. separator .. "25" then - return finale.MEASUREMENTUNIT_INCHES - elseif str.LuaString == "0" .. separator .. "635" then - return finale.MEASUREMENTUNIT_CENTIMETERS - elseif str.LuaString == "18" then - return finale.MEASUREMENTUNIT_POINTS - elseif str.LuaString == "1p6" then - return finale.MEASUREMENTUNIT_PICAS - elseif str.LuaString == "3" then - return finale.MEASUREMENTUNIT_SPACES - end -end - - - - -function expression_baseline_below_move_up() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - baselines:LoadAllForSystem(finale.BASELINEMODE_EXPRESSIONBELOW, i) - for j = start_slot, end_slot do - bl = baselines:AssureSavedStaff(finale.BASELINEMODE_EXPRESSIONBELOW, i, region:CalcStaffNumber(j)) - bl.VerticalOffset = bl.VerticalOffset + measurement.convert_to_EVPUs("1s") - bl:Save() - end - end -end - -expression_baseline_below_move_up() diff --git a/dist/expression_baseline_below_reset.lua b/dist/expression_baseline_below_reset.lua deleted file mode 100644 index 6a6ac321..00000000 --- a/dist/expression_baseline_below_reset.lua +++ /dev/null @@ -1,39 +0,0 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Version = "1.0" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "July 7, 2021" - finaleplugin.CategoryTags = "Expression" - finaleplugin.AuthorURL = "http://robertgpatterson.com" - return "Reset Expression Baseline Below", "Reset Expression Baseline Below", - "Resets the selected expression below baselines" -end - -function expression_baseline_below_reset() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - baselines:LoadAllForSystem(finale.BASELINEMODE_EXPRESSIONBELOW, i) - for baseline in each(baselines) do - local baseline_slot = region:CalcSlotNumber(baseline.Staff) - if (start_slot <= baseline_slot) and (baseline_slot <= end_slot) then - baseline:DeleteData() - end - end -end -end - -expression_baseline_below_reset() diff --git a/dist/expression_baseline_move_down.lua b/dist/expression_baseline_move_down.lua deleted file mode 100644 index 9478ec6e..00000000 --- a/dist/expression_baseline_move_down.lua +++ /dev/null @@ -1,199 +0,0 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Version = "1.0.2" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "June 12, 2020" - finaleplugin.CategoryTags = "Expression" - finaleplugin.AuthorURL = "https://nickmazuk.com" - return "Move Expression Baseline Down", "Move Expression Baseline Down", - "Moves the selected expression above baseline down one space" -end - ---[[ -$module measurement -]] -- -local measurement = {} - -local unit_names = { - [finale.MEASUREMENTUNIT_EVPUS] = "EVPUs", - [finale.MEASUREMENTUNIT_INCHES] = "Inches", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "Centimeters", - [finale.MEASUREMENTUNIT_POINTS] = "Points", - [finale.MEASUREMENTUNIT_PICAS] = "Picas", - [finale.MEASUREMENTUNIT_SPACES] = "Spaces", -} - -local unit_suffixes = { - [finale.MEASUREMENTUNIT_EVPUS] = "e", - [finale.MEASUREMENTUNIT_INCHES] = "i", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "c", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "p", - [finale.MEASUREMENTUNIT_SPACES] = "s", -} - -local unit_abbreviations = { - [finale.MEASUREMENTUNIT_EVPUS] = "ev", - [finale.MEASUREMENTUNIT_INCHES] = "in", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "cm", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "pc", - [finale.MEASUREMENTUNIT_SPACES] = "sp", -} - ---[[ -% convert_to_EVPUs - -Converts the specified string into EVPUs. Like text boxes in Finale, this supports -the usage of units at the end of the string. The following are a few examples: - -- `12s` => 288 (12 spaces is 288 EVPUs) -- `8.5i` => 2448 (8.5 inches is 2448 EVPUs) -- `10cm` => 1133 (10 centimeters is 1133 EVPUs) -- `10mm` => 113 (10 millimeters is 113 EVPUs) -- `1pt` => 4 (1 point is 4 EVPUs) -- `2.5p` => 120 (2.5 picas is 120 EVPUs) - -Read the [Finale User Manual](https://usermanuals.finalemusic.com/FinaleMac/Content/Finale/def-equivalents.htm#overriding-global-measurement-units) -for more details about measurement units in Finale. - -@ text (string) the string to convert -: (number) the converted number of EVPUs -]] -function measurement.convert_to_EVPUs(text) - local str = finale.FCString() - str.LuaString = text - return str:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) -end - ---[[ -% get_unit_name - -Returns the name of a measurement unit. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_name(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_names[unit] -end - ---[[ -% get_unit_suffix - -Returns the measurement unit's suffix. Suffixes can be used to force the text value (eg in `FCString` or `FCCtrlEdit`) to be treated as being from a particular measurement unit -Note that although this method returns a "p" for Picas, the fractional part goes after the "p" (eg `1p6`), so in practice it may be that no suffix is needed. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_suffix(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_suffixes[unit] -end - ---[[ -% get_unit_abbreviation - -Returns measurement unit abbreviations that are more human-readable than Finale's internal suffixes. -Abbreviations are also compatible with the internal ones because Finale discards everything after the first letter that isn't part of the suffix. - -For example: -```lua -local str_internal = finale.FCString() -str.LuaString = "2i" - -local str_display = finale.FCString() -str.LuaString = "2in" - -print(str_internal:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) == str_display:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT)) -- true -``` - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_abbreviation(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_abbreviations[unit] -end - ---[[ -% is_valid_unit - -Checks if a number is equal to one of the finale MEASUREMENTUNIT constants. - -@ unit (number) The unit to check. -: (boolean) `true` if valid, `false` if not. -]] -function measurement.is_valid_unit(unit) - return unit_names[unit] and true or false -end - ---[[ -% get_real_default_unit - -Resolves `finale.MEASUREMENTUNIT_DEFAULT` to the value of one of the other `MEASUREMENTUNIT` constants. - -: (number) -]] -function measurement.get_real_default_unit() - local str = finale.FCString() - finenv.UI():GetDecimalSeparator(str) - local separator = str.LuaString - str:SetMeasurement(72, finale.MEASUREMENTUNIT_DEFAULT) - - if str.LuaString == "72" then - return finale.MEASUREMENTUNIT_EVPUS - elseif str.LuaString == "0" .. separator .. "25" then - return finale.MEASUREMENTUNIT_INCHES - elseif str.LuaString == "0" .. separator .. "635" then - return finale.MEASUREMENTUNIT_CENTIMETERS - elseif str.LuaString == "18" then - return finale.MEASUREMENTUNIT_POINTS - elseif str.LuaString == "1p6" then - return finale.MEASUREMENTUNIT_PICAS - elseif str.LuaString == "3" then - return finale.MEASUREMENTUNIT_SPACES - end -end - - - - -function expression_baseline_move_down() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - baselines:LoadAllForSystem(finale.BASELINEMODE_EXPRESSIONABOVE, i) - for j = start_slot, end_slot do - bl = baselines:AssureSavedStaff(finale.BASELINEMODE_EXPRESSIONABOVE, i, region:CalcStaffNumber(j)) - bl.VerticalOffset = bl.VerticalOffset - measurement.convert_to_EVPUs("1s") - bl:Save() - end - end -end - -expression_baseline_move_down() diff --git a/dist/expression_baseline_move_up.lua b/dist/expression_baseline_move_up.lua deleted file mode 100644 index ca49ac8b..00000000 --- a/dist/expression_baseline_move_up.lua +++ /dev/null @@ -1,199 +0,0 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Version = "1.0.2" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "June 12, 2020" - finaleplugin.CategoryTags = "Expression" - finaleplugin.AuthorURL = "https://nickmazuk.com" - return "Move Expression Baseline Up", "Move Expression Baseline Up", - "Moves the selected expression above baseline up one space" -end - ---[[ -$module measurement -]] -- -local measurement = {} - -local unit_names = { - [finale.MEASUREMENTUNIT_EVPUS] = "EVPUs", - [finale.MEASUREMENTUNIT_INCHES] = "Inches", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "Centimeters", - [finale.MEASUREMENTUNIT_POINTS] = "Points", - [finale.MEASUREMENTUNIT_PICAS] = "Picas", - [finale.MEASUREMENTUNIT_SPACES] = "Spaces", -} - -local unit_suffixes = { - [finale.MEASUREMENTUNIT_EVPUS] = "e", - [finale.MEASUREMENTUNIT_INCHES] = "i", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "c", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "p", - [finale.MEASUREMENTUNIT_SPACES] = "s", -} - -local unit_abbreviations = { - [finale.MEASUREMENTUNIT_EVPUS] = "ev", - [finale.MEASUREMENTUNIT_INCHES] = "in", - [finale.MEASUREMENTUNIT_CENTIMETERS] = "cm", - [finale.MEASUREMENTUNIT_POINTS] = "pt", - [finale.MEASUREMENTUNIT_PICAS] = "pc", - [finale.MEASUREMENTUNIT_SPACES] = "sp", -} - ---[[ -% convert_to_EVPUs - -Converts the specified string into EVPUs. Like text boxes in Finale, this supports -the usage of units at the end of the string. The following are a few examples: - -- `12s` => 288 (12 spaces is 288 EVPUs) -- `8.5i` => 2448 (8.5 inches is 2448 EVPUs) -- `10cm` => 1133 (10 centimeters is 1133 EVPUs) -- `10mm` => 113 (10 millimeters is 113 EVPUs) -- `1pt` => 4 (1 point is 4 EVPUs) -- `2.5p` => 120 (2.5 picas is 120 EVPUs) - -Read the [Finale User Manual](https://usermanuals.finalemusic.com/FinaleMac/Content/Finale/def-equivalents.htm#overriding-global-measurement-units) -for more details about measurement units in Finale. - -@ text (string) the string to convert -: (number) the converted number of EVPUs -]] -function measurement.convert_to_EVPUs(text) - local str = finale.FCString() - str.LuaString = text - return str:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) -end - ---[[ -% get_unit_name - -Returns the name of a measurement unit. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_name(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_names[unit] -end - ---[[ -% get_unit_suffix - -Returns the measurement unit's suffix. Suffixes can be used to force the text value (eg in `FCString` or `FCCtrlEdit`) to be treated as being from a particular measurement unit -Note that although this method returns a "p" for Picas, the fractional part goes after the "p" (eg `1p6`), so in practice it may be that no suffix is needed. - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_suffix(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_suffixes[unit] -end - ---[[ -% get_unit_abbreviation - -Returns measurement unit abbreviations that are more human-readable than Finale's internal suffixes. -Abbreviations are also compatible with the internal ones because Finale discards everything after the first letter that isn't part of the suffix. - -For example: -```lua -local str_internal = finale.FCString() -str.LuaString = "2i" - -local str_display = finale.FCString() -str.LuaString = "2in" - -print(str_internal:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT) == str_display:GetMeasurement(finale.MEASUREMENTUNIT_DEFAULT)) -- true -``` - -@ unit (number) A finale MEASUREMENTUNIT constant. -: (string) -]] -function measurement.get_unit_abbreviation(unit) - if unit == finale.MEASUREMENTUNIT_DEFAULT then - unit = measurement.get_real_default_unit() - end - - return unit_abbreviations[unit] -end - ---[[ -% is_valid_unit - -Checks if a number is equal to one of the finale MEASUREMENTUNIT constants. - -@ unit (number) The unit to check. -: (boolean) `true` if valid, `false` if not. -]] -function measurement.is_valid_unit(unit) - return unit_names[unit] and true or false -end - ---[[ -% get_real_default_unit - -Resolves `finale.MEASUREMENTUNIT_DEFAULT` to the value of one of the other `MEASUREMENTUNIT` constants. - -: (number) -]] -function measurement.get_real_default_unit() - local str = finale.FCString() - finenv.UI():GetDecimalSeparator(str) - local separator = str.LuaString - str:SetMeasurement(72, finale.MEASUREMENTUNIT_DEFAULT) - - if str.LuaString == "72" then - return finale.MEASUREMENTUNIT_EVPUS - elseif str.LuaString == "0" .. separator .. "25" then - return finale.MEASUREMENTUNIT_INCHES - elseif str.LuaString == "0" .. separator .. "635" then - return finale.MEASUREMENTUNIT_CENTIMETERS - elseif str.LuaString == "18" then - return finale.MEASUREMENTUNIT_POINTS - elseif str.LuaString == "1p6" then - return finale.MEASUREMENTUNIT_PICAS - elseif str.LuaString == "3" then - return finale.MEASUREMENTUNIT_SPACES - end -end - - - - -function expression_baseline_move_up() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - baselines:LoadAllForSystem(finale.BASELINEMODE_EXPRESSIONABOVE, i) - for j = start_slot, end_slot do - bl = baselines:AssureSavedStaff(finale.BASELINEMODE_EXPRESSIONABOVE, i, region:CalcStaffNumber(j)) - bl.VerticalOffset = bl.VerticalOffset + measurement.convert_to_EVPUs("1s") - bl:Save() - end - end -end - -expression_baseline_move_up() diff --git a/dist/expression_baseline_reset.lua b/dist/expression_baseline_reset.lua deleted file mode 100644 index 300eb558..00000000 --- a/dist/expression_baseline_reset.lua +++ /dev/null @@ -1,39 +0,0 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Version = "1.0" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "July 7, 2021" - finaleplugin.CategoryTags = "Expression" - finaleplugin.AuthorURL = "http://robertgpatterson.com" - return "Reset Expression Baseline", "Reset Expression Baseline", - "Resets the selected expression above baselines" -end - -function expression_baseline_reset() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - baselines:LoadAllForSystem(finale.BASELINEMODE_EXPRESSIONABOVE, i) - for baseline in each(baselines) do - local baseline_slot = region:CalcSlotNumber(baseline.Staff) - if (start_slot <= baseline_slot) and (baseline_slot <= end_slot) then - baseline:DeleteData() - end - end -end -end - -expression_baseline_reset() diff --git a/dist/expression_find_orphaned_definitions.lua b/dist/expression_find_orphaned_definitions.lua index 85fcf017..b8cad5f6 100644 --- a/dist/expression_find_orphaned_definitions.lua +++ b/dist/expression_find_orphaned_definitions.lua @@ -1,112 +1,6 @@ -function plugindef() - -- This function and the 'finaleplugin' namespace - -- are both reserved for the plug-in definition. - finaleplugin.Author = "Robert Patterson" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0" - finaleplugin.Date = "June 27, 2020" - finaleplugin.CategoryTags = "Expression" - finaleplugin.Notes = [[ +function plugindef()finaleplugin.Author="Robert Patterson"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0"finaleplugin.Date="June 27, 2020"finaleplugin.CategoryTags="Expression"finaleplugin.Notes=[[ The Expression Selection Dialog expects expression definitions to be stored sequentially and stops looking for definitions once the next value is not found. However, Finale can leave orphaned expression definitions with higher values. These are inaccessible unless you add in dummy expressions to fill in the gaps. This script builds a report of any such expression definitions. - ]] - return "Expression Find Orphans", "Expression Find Orphans", - "Reports any orphaned expression definitions not visible in the Expression Selection Dialog." -end - - -local new_line_string = "\n" - --- :LoadAll() suffers from the same problem that the Expression Selection Dialog does. It stops looking once it hits a gap. --- So search all possible values. (It turns out attempting to load non-existent values is not a noticable performance hit.) - -local max_value_to_search = 32767 - -local get_report_string_for_orphans = function(orphaned_exps, is_for_shape) - local type_string = "Text Expression" - if is_for_shape then - type_string = "Shape Expression" - end - local report_string = "" - local is_first = true - for k, v in pairs(orphaned_exps) do - local exp_def = nil - if is_for_shape then - exp_def = finale.FCShapeExpressionDef() - else - exp_def = finale.FCTextExpressionDef() - end - if exp_def:Load(v) then - if not is_first then - report_string = report_string .. new_line_string - end - is_first = false - report_string = report_string .. type_string .. " " .. exp_def.ItemNo - if not is_for_shape then - local text_block = finale.FCTextBlock() - if text_block:Load(exp_def.TextID) then - local raw_text = text_block:CreateRawTextString() - if nil ~= raw_text then - raw_text:TrimEnigmaFontTags() - report_string = report_string .. " " .. raw_text.LuaString - end - end - end - end - end - return report_string -end - -local expression_find_orphans_for_type = function(is_for_shape) - local exp_def = nil - if is_for_shape then - exp_def = finale.FCShapeExpressionDef() - else - exp_def = finale.FCTextExpressionDef() - end - local count = 0 - local max_valid = 0 - local max_found = 0 - local orphaned_exps = { } - for try_id = 1, max_value_to_search do - if exp_def:Load(try_id) then - max_found = exp_def.ItemNo - count = count + 1 - if count ~= exp_def.ItemNo then - table.insert(orphaned_exps, exp_def.ItemNo) - else - max_valid = count - end - end - end - return orphaned_exps, max_valid, max_found -end - -function expression_find_orphaned_definitions() - local orphaned_text_exps, text_max_valid, text_max_found = expression_find_orphans_for_type(false) - local orphaned_shape_exps, shape_max_valid, shape_max_found = expression_find_orphans_for_type(true) - local got_orphan = false - local report_string = "" - if #orphaned_text_exps > 0 then - got_orphan = true - report_string = report_string .. get_report_string_for_orphans(orphaned_text_exps, false) - end - if #orphaned_shape_exps > 0 then - if got_orphan then -- if we found text exps as well - report_string = report_string .. new_line_string .. new_line_string - else - got_orphan = true - end - report_string = report_string .. get_report_string_for_orphans(orphaned_shape_exps, true) - end - if got_orphan then - report_string = report_string .. new_line_string .. new_line_string .. "Max Valid Text = " .. text_max_valid .. ". Max Valid Shape = " .. shape_max_valid .. "." - finenv.UI():AlertInfo(report_string, "Found Orphaned Expressions:") - else - finenv.UI():AlertInfo("", "No Orphaned Expressions Found") - end -end - -expression_find_orphaned_definitions() + ]]return"Expression Find Orphans","Expression Find Orphans","Reports any orphaned expression definitions not visible in the Expression Selection Dialog."end;local a="\n"local b=32767;local c=function(d,e)local f="Text Expression"if e then f="Shape Expression"end;local g=""local h=true;for i,j in pairs(d)do local k=nil;if e then k=finale.FCShapeExpressionDef()else k=finale.FCTextExpressionDef()end;if k:Load(j)then if not h then g=g..a end;h=false;g=g..f.." "..k.ItemNo;if not e then local l=finale.FCTextBlock()if l:Load(k.TextID)then local m=l:CreateRawTextString()if nil~=m then m:TrimEnigmaFontTags()g=g.." "..m.LuaString end end end end end;return g end;local n=function(e)local k=nil;if e then k=finale.FCShapeExpressionDef()else k=finale.FCTextExpressionDef()end;local o=0;local p=0;local q=0;local d={}for r=1,b do if k:Load(r)then q=k.ItemNo;o=o+1;if o~=k.ItemNo then table.insert(d,k.ItemNo)else p=o end end end;return d,p,q end;function expression_find_orphaned_definitions()local s,t,u=n(false)local v,w,x=n(true)local y=false;local g=""if#s>0 then y=true;g=g..c(s,false)end;if#v>0 then if y then g=g..a..a else y=true end;g=g..c(v,true)end;if y then g=g..a..a.."Max Valid Text = "..t..". Max Valid Shape = "..w.."."finenv.UI():AlertInfo(g,"Found Orphaned Expressions:")else finenv.UI():AlertInfo("","No Orphaned Expressions Found")end end;expression_find_orphaned_definitions() \ No newline at end of file diff --git a/dist/layer_clear_selective.lua b/dist/layer_clear_selective.lua index 95768ff7..83aab8c4 100644 --- a/dist/layer_clear_selective.lua +++ b/dist/layer_clear_selective.lua @@ -1,147 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Carl Vine" - finaleplugin.AuthorURL = "http://carlvine.com/?cv=lua" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "v1.04" - finaleplugin.Date = "2022/06/15" - finaleplugin.CategoryTags = "Note" - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Carl Vine"finaleplugin.AuthorURL="http://carlvine.com/?cv=lua"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="v1.04"finaleplugin.Date="2022/06/15"finaleplugin.CategoryTags="Note"finaleplugin.Notes=[[ Clear all music from the chosen layer in the surrently selected region. (Note that all of a measure's layer will be cleared even if it is partially selected). - ]] - return "Clear layer selective", "Clear layer selective", "Clear the chosen layer" -end - --- RetainLuaState will return global variable: clear_layer_number ---[[ -$module Layer -]] -- -local layer = {} - ---[[ -% copy - -Duplicates the notes from the source layer to the destination. The source layer remains untouched. - -@ region (FCMusicRegion) the region to be copied -@ source_layer (number) the number (1-4) of the layer to duplicate -@ destination_layer (number) the number (1-4) of the layer to be copied to -]] -function layer.copy(region, source_layer, destination_layer) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - source_layer = source_layer - 1 - destination_layer = destination_layer - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentry_source_layer = finale.FCNoteEntryLayer(source_layer, staffNum, start, stop) - noteentry_source_layer:Load() - local noteentry_destination_layer = noteentry_source_layer:CreateCloneEntries( - destination_layer, staffNum, start) - noteentry_destination_layer:Save() - noteentry_destination_layer:CloneTuplets(noteentry_source_layer) - noteentry_destination_layer:Save() - end -end -- function layer_copy - ---[[ -% clear - -Clears all entries from a given layer. - -@ region (FCMusicRegion) the region to be cleared -@ layer_to_clear (number) the number (1-4) of the layer to clear -]] -function layer.clear(region, layer_to_clear) - layer_to_clear = layer_to_clear - 1 -- Turn 1 based layer to 0 based layer - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer = finale.FCNoteEntryLayer(layer_to_clear, staffNum, start, stop) - noteentrylayer:Load() - noteentrylayer:ClearAllEntries() - end -end - ---[[ -% swap - -Swaps the entries from two different layers (e.g. 1-->2 and 2-->1). - -@ region (FCMusicRegion) the region to be swapped -@ swap_a (number) the number (1-4) of the first layer to be swapped -@ swap_b (number) the number (1-4) of the second layer to be swapped -]] -function layer.swap(region, swap_a, swap_b) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - -- Set layers for 0 based - swap_a = swap_a - 1 - swap_b = swap_b - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer_1 = finale.FCNoteEntryLayer(swap_a, staffNum, start, stop) - noteentrylayer_1:Load() - noteentrylayer_1.LayerIndex = swap_b - -- - local noteentrylayer_2 = finale.FCNoteEntryLayer(swap_b, staffNum, start, stop) - noteentrylayer_2:Load() - noteentrylayer_2.LayerIndex = swap_a - noteentrylayer_1:Save() - noteentrylayer_2:Save() - end -end - - - - -function get_user_choice() - local vertical = 10 - local horizontal = 110 - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 -- extra y-offset for Mac text box - - local dialog = finale.FCCustomWindow() - local str = finale.FCString() - str.LuaString = plugindef() - dialog:SetTitle(str) - - str.LuaString = "Clear Layer (1-4):" - local static = dialog:CreateStatic(0, vertical) - static:SetText(str) - static:SetWidth(horizontal) - - local layer_choice = dialog:CreateEdit(horizontal, vertical - mac_offset) - layer_choice:SetInteger(clear_layer_number or 1) -- default layer 1 - layer_choice:SetWidth(50) - - dialog:CreateOkButton() - dialog:CreateCancelButton() - return (dialog:ExecuteModal(nil) == finale.EXECMODAL_OK), layer_choice:GetInteger() -end - -function clear_layers() - local is_ok = false - is_ok, clear_layer_number = get_user_choice() - if not is_ok then -- user cancelled - return - end - if clear_layer_number < 1 or clear_layer_number > 4 then - finenv.UI():AlertNeutral("script: " .. plugindef(), - "The layer number must be\nan integer between 1 and 4\n(not " .. clear_layer_number .. ")") - return - end - if finenv.RetainLuaState ~= nil then - finenv.RetainLuaState = true - end - layer.clear(finenv.Region(), clear_layer_number) -end - -clear_layers() + ]]return"Clear layer selective","Clear layer selective","Clear the chosen layer"end;local o=require("library.layer")function get_user_choice()local p=10;local q=110;local r=finenv.UI():IsOnMac()and 3 or 0;local s=finale.FCCustomWindow()local t=finale.FCString()t.LuaString=plugindef()s:SetTitle(t)t.LuaString="Clear Layer (1-4):"local u=s:CreateStatic(0,p)u:SetText(t)u:SetWidth(q)local v=s:CreateEdit(q,p-r)v:SetInteger(clear_layer_number or 1)v:SetWidth(50)s:CreateOkButton()s:CreateCancelButton()return s:ExecuteModal(nil)==finale.EXECMODAL_OK,v:GetInteger()end;function clear_layers()local w=false;w,clear_layer_number=get_user_choice()if not w then return end;if clear_layer_number<1 or clear_layer_number>4 then finenv.UI():AlertNeutral("script: "..plugindef(),"The layer number must be\nan integer between 1 and 4\n(not "..clear_layer_number..")")return end;if finenv.RetainLuaState~=nil then finenv.RetainLuaState=true end;o.clear(finenv.Region(),clear_layer_number)end;clear_layers()end)c("library.layer",function(require,n,c,d)local x={}function x.finale_version(y,z,A)local B=bit32.bor(bit32.lshift(math.floor(y),24),bit32.lshift(math.floor(z),20))if A then B=bit32.bor(B,math.floor(A))end;return B end;function x.group_overlaps_region(C,D)if D:IsFullDocumentSpan()then return true end;local E=false;local F=finale.FCSystemStaves()F:LoadAllForRegion(D)for G in each(F)do if C:ContainsStaff(G:GetStaff())then E=true;break end end;if not E then return false end;if C.StartMeasure>D.EndMeasure or C.EndMeasure0 then N=true;break end;L=L+1 end;if N then local O=finale.FCStaffSystem()O:Load(M:GetFirstSystem())return finale.FCCell(O.FirstMeasure,O.TopStaff)end;local P=finale.FCMusicRegion()P:SetFullDocument()return finale.FCCell(P.EndMeasure,P.EndStaff)end;function x.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local Q=finale.FCMusicRegion()Q:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),Q.StartStaff)end;return x.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function x.get_top_left_selected_or_visible_cell()local J=finenv.Region()if not J:IsEmpty()then return finale.FCCell(J.StartMeasure,J.StartStaff)end;return x.get_top_left_visible_cell()end;function x.is_default_measure_number_visible_on_cell(R,S,T,U)local V=finale.FCCurrentStaffSpec()if not V:LoadForCell(S,0)then return false end;if R:GetShowOnTopStaff()and S.Staff==T.TopStaff then return true end;if R:GetShowOnBottomStaff()and S.Staff==T:CalcBottomStaff()then return true end;if V.ShowMeasureNumbers then return not R:GetExcludeOtherStaves(U)end;return false end;function x.is_default_number_visible_and_left_aligned(R,S,W,U,X)if R.UseScoreInfoForParts then U=false end;if X and R:GetShowOnMultiMeasureRests(U)then if finale.MNALIGN_LEFT~=R:GetMultiMeasureAlignment(U)then return false end elseif S.Measure==W.FirstMeasure then if not R:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=R:GetStartAlignment(U)then return false end else if not R:GetShowMultiples(U)then return false end;if finale.MNALIGN_LEFT~=R:GetMultipleAlignment(U)then return false end end;return x.is_default_measure_number_visible_on_cell(R,S,W,U)end;function x.update_layout(Y,Z)Y=Y or 1;Z=Z or false;local _=finale.FCPage()if _:Load(Y)then _:UpdateLayout(Z)end end;function x.get_current_part()local a0=finale.FCParts()a0:LoadAll()return a0:GetCurrent()end;function x.get_page_format_prefs()local a1=x.get_current_part()local a2=finale.FCPageFormatPrefs()local a3=false;if a1:IsScore()then a3=a2:LoadScore()else a3=a2:LoadParts()end;return a2,a3 end;function x.get_smufl_metadata_file(a4)if not a4 then a4=finale.FCFontInfo()a4:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local a5=function(a6,a4)local a7=a6 .."/SMuFL/Fonts/"..a4.Name.."/"..a4.Name..".json"return io.open(a7,"r")end;local a8=""if finenv.UI():IsOnWindows()then a8=os.getenv("LOCALAPPDATA")else a8=os.getenv("HOME").."/Library/Application Support"end;local a9=a5(a8,a4)if nil~=a9 then return a9 end;local aa="/Library/Application Support"if finenv.UI():IsOnWindows()then aa=os.getenv("COMMONPROGRAMFILES")end;return a5(aa,a4)end;function x.is_font_smufl_font(a4)if not a4 then a4=finale.FCFontInfo()a4:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=x.finale_version(27,1)then if nil~=a4.IsSMuFLFont then return a4.IsSMuFLFont end end;local ab=x.get_smufl_metadata_file(a4)if nil~=ab then io.close(ab)return true end;return false end;function x.simple_input(ac,ad)local ae=finale.FCString()ae.LuaString=""local t=finale.FCString()local af=160;function format_ctrl(ag,ah,ai,aj)ag:SetHeight(ah)ag:SetWidth(ai)t.LuaString=aj;ag:SetText(t)end;title_width=string.len(ac)*6+54;if title_width>af then af=title_width end;text_width=string.len(ad)*6;if text_width>af then af=text_width end;t.LuaString=ac;local s=finale.FCCustomLuaWindow()s:SetTitle(t)local ak=s:CreateStatic(0,0)format_ctrl(ak,16,af,ad)local al=s:CreateEdit(0,20)format_ctrl(al,20,af,"")s:CreateOkButton()s:CreateCancelButton()function callback(ag)end;s:RegisterHandleCommand(callback)if s:ExecuteModal(nil)==finale.EXECMODAL_OK then ae.LuaString=al:GetText(ae)return ae.LuaString end end;function x.is_finale_object(am)return am and type(am)=="userdata"and am.ClassName and am.GetClassID and true or false end;function x.system_indent_set_to_prefs(W,a2)a2=a2 or x.get_page_format_prefs()local an=finale.FCMeasure()local ao=W.FirstMeasure==1;if not ao and an:Load(W.FirstMeasure)then if an.ShowFullNames then ao=true end end;if ao and a2.UseFirstSystemMargins then W.LeftMargin=a2.FirstSystemLeft else W.LeftMargin=a2.SystemLeft end;return W:Save()end;return x end)return a("__root") \ No newline at end of file diff --git a/dist/layer_hide.lua b/dist/layer_hide.lua index dccfb881..c00301ec 100644 --- a/dist/layer_hide.lua +++ b/dist/layer_hide.lua @@ -1,62 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.AuthorURL = "http://carlvine.com" - finaleplugin.Version = "v1.04" - finaleplugin.Date = "2022/05/30" - finaleplugin.AdditionalMenuOptions = [[ Layer Unhide ]] - finaleplugin.AdditionalUndoText = [[ Layer Unhide ]] - finaleplugin.AdditionalPrefixes = [[ unhide_layer = true ]] - finaleplugin.MinJWLuaVersion = 0.62 - finaleplugin.Notes = [[ +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.AuthorURL="http://carlvine.com"finaleplugin.Version="v1.04"finaleplugin.Date="2022/05/30"finaleplugin.AdditionalMenuOptions=[[ Layer Unhide ]]finaleplugin.AdditionalUndoText=[[ Layer Unhide ]]finaleplugin.AdditionalPrefixes=[[ unhide_layer = true ]]finaleplugin.MinJWLuaVersion=0.62;finaleplugin.Notes=[[ Hide the nominated layer, or all layers. RGPLua (0.62 and above) creates a companion menu item, Layer UNHIDE. - ]] - return "Layer Hide", "Layer Hide", "Hide selected layer(s) with complementary UNHIDE menu" -end - --- default to "hide" layer for "normal" operation -unhide_layer = unhide_layer or false - -function choose_layer_to_affect() - local dialog = finale.FCCustomWindow() - local str = finale.FCString() - local vertical = 10 - local horiz_offset = 120 - local edit_width = 50 - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 -- extra horizontal offset for Mac edit box - - str.LuaString = unhide_layer and "Layer Unhide" or "Layer Hide" - dialog:SetTitle(str) - str.LuaString = "Layer# 1-4 (0 = all):" - local static = dialog:CreateStatic(0, vertical) - static:SetText(str) - static:SetWidth(horiz_offset) - - local answer = dialog:CreateEdit(horiz_offset, vertical - mac_offset) - answer:SetInteger(0) -- set default layer ALL - answer:SetWidth(edit_width) - dialog:CreateOkButton() - dialog:CreateCancelButton() - return dialog:ExecuteModal(nil), answer:GetInteger() -end - -function change_state() - local ok, layer_number = choose_layer_to_affect() - if ok ~= finale.EXECMODAL_OK then -- user cancelled - return -- go home - end - if layer_number < 0 or layer_number > 4 then - finenv.UI():AlertNeutral( - "(script: " .. plugindef() .. ")", - "Layer number must be\nbetween 0 and 4\n(not " .. layer_number ..")" - ) - return -- go home - end - for entry in eachentrysaved(finenv.Region(), layer_number) do - entry.Visible = unhide_layer - end - -end - -change_state() + ]]return"Layer Hide","Layer Hide","Hide selected layer(s) with complementary UNHIDE menu"end;unhide_layer=unhide_layer or false;function choose_layer_to_affect()local a=finale.FCCustomWindow()local b=finale.FCString()local c=10;local d=120;local e=50;local f=finenv.UI():IsOnMac()and 3 or 0;b.LuaString=unhide_layer and"Layer Unhide"or"Layer Hide"a:SetTitle(b)b.LuaString="Layer# 1-4 (0 = all):"local g=a:CreateStatic(0,c)g:SetText(b)g:SetWidth(d)local h=a:CreateEdit(d,c-f)h:SetInteger(0)h:SetWidth(e)a:CreateOkButton()a:CreateCancelButton()return a:ExecuteModal(nil),h:GetInteger()end;function change_state()local i,j=choose_layer_to_affect()if i~=finale.EXECMODAL_OK then return end;if j<0 or j>4 then finenv.UI():AlertNeutral("(script: "..plugindef()..")","Layer number must be\nbetween 0 and 4\n(not "..j..")")return end;for k in eachentrysaved(finenv.Region(),j)do k.Visible=unhide_layer end end;change_state() \ No newline at end of file diff --git a/dist/layer_mute.lua b/dist/layer_mute.lua index 23faee75..646297c5 100644 --- a/dist/layer_mute.lua +++ b/dist/layer_mute.lua @@ -1,62 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.AuthorURL = "http://carlvine.com" - finaleplugin.Version = "v1.04" - finaleplugin.Date = "2022/05/30" - finaleplugin.AdditionalMenuOptions = [[ Layer Unmute ]] - finaleplugin.AdditionalUndoText = [[ Layer Unmute ]] - finaleplugin.AdditionalPrefixes = [[ unmute_layer = true ]] - finaleplugin.MinJWLuaVersion = 0.62 - finaleplugin.Notes = [[ +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.AuthorURL="http://carlvine.com"finaleplugin.Version="v1.04"finaleplugin.Date="2022/05/30"finaleplugin.AdditionalMenuOptions=[[ Layer Unmute ]]finaleplugin.AdditionalUndoText=[[ Layer Unmute ]]finaleplugin.AdditionalPrefixes=[[ unmute_layer = true ]]finaleplugin.MinJWLuaVersion=0.62;finaleplugin.Notes=[[ Mute the nominated layer, or all layers. RGPLua (0.62 and above) creates a companion menu item, Layer UNMUTE. - ]] - return "Layer Mute", "Layer Mute", "Mute selected layer(s) with complementary UMMUTE menu" -end - --- default to "mute" layer for "normal" operation -unmute_layer = unmute_layer or false - -function choose_layer_to_affect() - local dialog = finale.FCCustomWindow() - local str = finale.FCString() - local vertical = 10 - local horiz_offset = 120 - local edit_width = 50 - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 -- extra horizontal offset for Mac edit box - - str.LuaString = unmute_layer and "Layer Unmute" or "Layer Mute" - dialog:SetTitle(str) - str.LuaString = "Layer# 1-4 (0 = all):" - local static = dialog:CreateStatic(0, vertical) - static:SetText(str) - static:SetWidth(horiz_offset) - - local answer = dialog:CreateEdit(horiz_offset, vertical - mac_offset) - answer:SetInteger(0) -- set default layer ALL - answer:SetWidth(edit_width) - dialog:CreateOkButton() - dialog:CreateCancelButton() - return dialog:ExecuteModal(nil), answer:GetInteger() -end - -function change_state() - local ok, layer_number = choose_layer_to_affect() - if ok ~= finale.EXECMODAL_OK then -- user cancelled - return -- go home - end - if layer_number < 0 or layer_number > 4 then - finenv.UI():AlertNeutral( - "(script: " .. plugindef() .. ")", - "Layer number must be\nbetween 0 and 4\n(not " .. layer_number ..")" - ) - return -- go home - end - for entry in eachentrysaved(finenv.Region(), layer_number) do - entry.Playback = unmute_layer - end - -end - -change_state() + ]]return"Layer Mute","Layer Mute","Mute selected layer(s) with complementary UMMUTE menu"end;unmute_layer=unmute_layer or false;function choose_layer_to_affect()local a=finale.FCCustomWindow()local b=finale.FCString()local c=10;local d=120;local e=50;local f=finenv.UI():IsOnMac()and 3 or 0;b.LuaString=unmute_layer and"Layer Unmute"or"Layer Mute"a:SetTitle(b)b.LuaString="Layer# 1-4 (0 = all):"local g=a:CreateStatic(0,c)g:SetText(b)g:SetWidth(d)local h=a:CreateEdit(d,c-f)h:SetInteger(0)h:SetWidth(e)a:CreateOkButton()a:CreateCancelButton()return a:ExecuteModal(nil),h:GetInteger()end;function change_state()local i,j=choose_layer_to_affect()if i~=finale.EXECMODAL_OK then return end;if j<0 or j>4 then finenv.UI():AlertNeutral("(script: "..plugindef()..")","Layer number must be\nbetween 0 and 4\n(not "..j..")")return end;for k in eachentrysaved(finenv.Region(),j)do k.Playback=unmute_layer end end;change_state() \ No newline at end of file diff --git a/dist/layers_swap_1_2.lua b/dist/layers_swap_1_2.lua index 9eb878c1..1195d588 100644 --- a/dist/layers_swap_1_2.lua +++ b/dist/layers_swap_1_2.lua @@ -1,107 +1,3 @@ -function plugindef() - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0.0" - finaleplugin.Date = "March 26, 2022" - finaleplugin.CategoryTags = "Playback" - finaleplugin.AuthorURL = "https://nickmazuk.com" - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.Author="Nick Mazuk"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0.0"finaleplugin.Date="March 26, 2022"finaleplugin.CategoryTags="Playback"finaleplugin.AuthorURL="https://nickmazuk.com"finaleplugin.Notes=[[ Swaps layers 1 and 2 for the selected region. - ]] - return "Swap layers 1 and 2", "Swap layers 1 and 2", "Swaps layers 1 and 2" -end - ---[[ -$module Layer -]] -- -local layers = {} - ---[[ -% copy - -Duplicates the notes from the source layer to the destination. The source layer remains untouched. - -@ region (FCMusicRegion) the region to be copied -@ source_layer (number) the number (1-4) of the layer to duplicate -@ destination_layer (number) the number (1-4) of the layer to be copied to -]] -function layers.copy(region, source_layer, destination_layer) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - source_layer = source_layer - 1 - destination_layer = destination_layer - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentry_source_layer = finale.FCNoteEntryLayer(source_layer, staffNum, start, stop) - noteentry_source_layer:Load() - local noteentry_destination_layer = noteentry_source_layer:CreateCloneEntries( - destination_layer, staffNum, start) - noteentry_destination_layer:Save() - noteentry_destination_layer:CloneTuplets(noteentry_source_layer) - noteentry_destination_layer:Save() - end -end -- function layer_copy - ---[[ -% clear - -Clears all entries from a given layer. - -@ region (FCMusicRegion) the region to be cleared -@ layer_to_clear (number) the number (1-4) of the layer to clear -]] -function layers.clear(region, layer_to_clear) - layer_to_clear = layer_to_clear - 1 -- Turn 1 based layer to 0 based layer - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer = finale.FCNoteEntryLayer(layer_to_clear, staffNum, start, stop) - noteentrylayer:Load() - noteentrylayer:ClearAllEntries() - end -end - ---[[ -% swap - -Swaps the entries from two different layers (e.g. 1-->2 and 2-->1). - -@ region (FCMusicRegion) the region to be swapped -@ swap_a (number) the number (1-4) of the first layer to be swapped -@ swap_b (number) the number (1-4) of the second layer to be swapped -]] -function layers.swap(region, swap_a, swap_b) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - -- Set layers for 0 based - swap_a = swap_a - 1 - swap_b = swap_b - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer_1 = finale.FCNoteEntryLayer(swap_a, staffNum, start, stop) - noteentrylayer_1:Load() - noteentrylayer_1.LayerIndex = swap_b - -- - local noteentrylayer_2 = finale.FCNoteEntryLayer(swap_b, staffNum, start, stop) - noteentrylayer_2:Load() - noteentrylayer_2.LayerIndex = swap_a - noteentrylayer_1:Save() - noteentrylayer_2:Save() - end -end - - - - -function layers_swap_1_2() - layers.swap(finenv.Region(), 1, 2) -end - -layers_swap_1_2() + ]]return"Swap layers 1 and 2","Swap layers 1 and 2","Swaps layers 1 and 2"end;local o=require("library.layer")function layers_swap_1_2()o.swap(finenv.Region(),1,2)end;layers_swap_1_2()end)c("library.layer",function(require,n,c,d)local p={}function p.finale_version(q,r,s)local t=bit32.bor(bit32.lshift(math.floor(q),24),bit32.lshift(math.floor(r),20))if s then t=bit32.bor(t,math.floor(s))end;return t end;function p.group_overlaps_region(u,v)if v:IsFullDocumentSpan()then return true end;local w=false;local x=finale.FCSystemStaves()x:LoadAllForRegion(v)for y in each(x)do if u:ContainsStaff(y:GetStaff())then w=true;break end end;if not w then return false end;if u.StartMeasure>v.EndMeasure or u.EndMeasure0 then F=true;break end;D=D+1 end;if F then local G=finale.FCStaffSystem()G:Load(E:GetFirstSystem())return finale.FCCell(G.FirstMeasure,G.TopStaff)end;local H=finale.FCMusicRegion()H:SetFullDocument()return finale.FCCell(H.EndMeasure,H.EndStaff)end;function p.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local I=finale.FCMusicRegion()I:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),I.StartStaff)end;return p.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function p.get_top_left_selected_or_visible_cell()local B=finenv.Region()if not B:IsEmpty()then return finale.FCCell(B.StartMeasure,B.StartStaff)end;return p.get_top_left_visible_cell()end;function p.is_default_measure_number_visible_on_cell(J,K,L,M)local N=finale.FCCurrentStaffSpec()if not N:LoadForCell(K,0)then return false end;if J:GetShowOnTopStaff()and K.Staff==L.TopStaff then return true end;if J:GetShowOnBottomStaff()and K.Staff==L:CalcBottomStaff()then return true end;if N.ShowMeasureNumbers then return not J:GetExcludeOtherStaves(M)end;return false end;function p.is_default_number_visible_and_left_aligned(J,K,O,M,P)if J.UseScoreInfoForParts then M=false end;if P and J:GetShowOnMultiMeasureRests(M)then if finale.MNALIGN_LEFT~=J:GetMultiMeasureAlignment(M)then return false end elseif K.Measure==O.FirstMeasure then if not J:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=J:GetStartAlignment(M)then return false end else if not J:GetShowMultiples(M)then return false end;if finale.MNALIGN_LEFT~=J:GetMultipleAlignment(M)then return false end end;return p.is_default_measure_number_visible_on_cell(J,K,O,M)end;function p.update_layout(Q,R)Q=Q or 1;R=R or false;local S=finale.FCPage()if S:Load(Q)then S:UpdateLayout(R)end end;function p.get_current_part()local T=finale.FCParts()T:LoadAll()return T:GetCurrent()end;function p.get_page_format_prefs()local U=p.get_current_part()local V=finale.FCPageFormatPrefs()local W=false;if U:IsScore()then W=V:LoadScore()else W=V:LoadParts()end;return V,W end;function p.get_smufl_metadata_file(X)if not X then X=finale.FCFontInfo()X:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local Y=function(Z,X)local _=Z.."/SMuFL/Fonts/"..X.Name.."/"..X.Name..".json"return io.open(_,"r")end;local a0=""if finenv.UI():IsOnWindows()then a0=os.getenv("LOCALAPPDATA")else a0=os.getenv("HOME").."/Library/Application Support"end;local a1=Y(a0,X)if nil~=a1 then return a1 end;local a2="/Library/Application Support"if finenv.UI():IsOnWindows()then a2=os.getenv("COMMONPROGRAMFILES")end;return Y(a2,X)end;function p.is_font_smufl_font(X)if not X then X=finale.FCFontInfo()X:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=p.finale_version(27,1)then if nil~=X.IsSMuFLFont then return X.IsSMuFLFont end end;local a3=p.get_smufl_metadata_file(X)if nil~=a3 then io.close(a3)return true end;return false end;function p.simple_input(a4,a5)local a6=finale.FCString()a6.LuaString=""local a7=finale.FCString()local a8=160;function format_ctrl(a9,aa,ab,ac)a9:SetHeight(aa)a9:SetWidth(ab)a7.LuaString=ac;a9:SetText(a7)end;title_width=string.len(a4)*6+54;if title_width>a8 then a8=title_width end;text_width=string.len(a5)*6;if text_width>a8 then a8=text_width end;a7.LuaString=a4;local ad=finale.FCCustomLuaWindow()ad:SetTitle(a7)local ae=ad:CreateStatic(0,0)format_ctrl(ae,16,a8,a5)local af=ad:CreateEdit(0,20)format_ctrl(af,20,a8,"")ad:CreateOkButton()ad:CreateCancelButton()function callback(a9)end;ad:RegisterHandleCommand(callback)if ad:ExecuteModal(nil)==finale.EXECMODAL_OK then a6.LuaString=af:GetText(a6)return a6.LuaString end end;function p.is_finale_object(ag)return ag and type(ag)=="userdata"and ag.ClassName and ag.GetClassID and true or false end;function p.system_indent_set_to_prefs(O,V)V=V or p.get_page_format_prefs()local ah=finale.FCMeasure()local ai=O.FirstMeasure==1;if not ai and ah:Load(O.FirstMeasure)then if ah.ShowFullNames then ai=true end end;if ai and V.UseFirstSystemMargins then O.LeftMargin=V.FirstSystemLeft else O.LeftMargin=V.SystemLeft end;return O:Save()end;return p end)return a("__root") \ No newline at end of file diff --git a/dist/layers_swap_selective.lua b/dist/layers_swap_selective.lua index f3857b7b..7137a98c 100644 --- a/dist/layers_swap_selective.lua +++ b/dist/layers_swap_selective.lua @@ -1,156 +1,3 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Carl Vine" - finaleplugin.AuthorURL = "http://carlvine.com" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "v0.50" - finaleplugin.Date = "2022/05/17" - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Carl Vine"finaleplugin.AuthorURL="http://carlvine.com"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="v0.50"finaleplugin.Date="2022/05/17"finaleplugin.Notes=[[ Swaps notes in the selected region between two layers chosen by the user. - ]] - return "Swap layers selective", "Swap layers selective", "Swap layers selectively" -end - ---[[ -$module Layer -]] -- -local layer = {} - ---[[ -% copy - -Duplicates the notes from the source layer to the destination. The source layer remains untouched. - -@ region (FCMusicRegion) the region to be copied -@ source_layer (number) the number (1-4) of the layer to duplicate -@ destination_layer (number) the number (1-4) of the layer to be copied to -]] -function layer.copy(region, source_layer, destination_layer) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - source_layer = source_layer - 1 - destination_layer = destination_layer - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentry_source_layer = finale.FCNoteEntryLayer(source_layer, staffNum, start, stop) - noteentry_source_layer:Load() - local noteentry_destination_layer = noteentry_source_layer:CreateCloneEntries( - destination_layer, staffNum, start) - noteentry_destination_layer:Save() - noteentry_destination_layer:CloneTuplets(noteentry_source_layer) - noteentry_destination_layer:Save() - end -end -- function layer_copy - ---[[ -% clear - -Clears all entries from a given layer. - -@ region (FCMusicRegion) the region to be cleared -@ layer_to_clear (number) the number (1-4) of the layer to clear -]] -function layer.clear(region, layer_to_clear) - layer_to_clear = layer_to_clear - 1 -- Turn 1 based layer to 0 based layer - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer = finale.FCNoteEntryLayer(layer_to_clear, staffNum, start, stop) - noteentrylayer:Load() - noteentrylayer:ClearAllEntries() - end -end - ---[[ -% swap - -Swaps the entries from two different layers (e.g. 1-->2 and 2-->1). - -@ region (FCMusicRegion) the region to be swapped -@ swap_a (number) the number (1-4) of the first layer to be swapped -@ swap_b (number) the number (1-4) of the second layer to be swapped -]] -function layer.swap(region, swap_a, swap_b) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - -- Set layers for 0 based - swap_a = swap_a - 1 - swap_b = swap_b - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer_1 = finale.FCNoteEntryLayer(swap_a, staffNum, start, stop) - noteentrylayer_1:Load() - noteentrylayer_1.LayerIndex = swap_b - -- - local noteentrylayer_2 = finale.FCNoteEntryLayer(swap_b, staffNum, start, stop) - noteentrylayer_2:Load() - noteentrylayer_2.LayerIndex = swap_a - noteentrylayer_1:Save() - noteentrylayer_2:Save() - end -end - - - - -function find_errors(layer_a, layer_b) - local error_message = "" - if layer_a < 1 or layer_a > 4 or layer_b < 1 or layer_b > 4 then - error_message = "Layer numbers must be\nintegers between 1 and 4" - elseif layer_a == layer_b then - error_message = "Please nominate two DIFFERENT layers" - end - if (error_message ~= "") then -- error dialog and exit - finenv.UI():AlertNeutral("(script: " .. plugindef() .. ")", error_message) - return true - end - return false -end - -function choose_layers_to_swap() - local current_vertical = 10 -- first vertical position - local vertical_step = 25 -- vertical step for each line - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 -- extra horizontal offset for Mac edit box - local horiz_offset = 110 - local edit_boxes = {} -- array of edit boxes for user data input - local string = finale.FCString() - local dialog = finale.FCCustomWindow() - - string.LuaString = plugindef() - dialog:SetTitle(string) - local texts = { -- words, default value - { "swap layer# (1-4):", 1 }, - { "with layer# (1-4):", 2 } - } - - for i,v in ipairs(texts) do - string.LuaString = v[1] - local static = dialog:CreateStatic(0, current_vertical) - static:SetText(string) - static:SetWidth(200) - edit_boxes[i] = dialog:CreateEdit(horiz_offset, current_vertical - mac_offset) - edit_boxes[i]:SetInteger(v[2]) - current_vertical = current_vertical + vertical_step - end - - dialog:CreateOkButton() - dialog:CreateCancelButton() - return dialog:ExecuteModal(nil), -- == 1 if Ok button pressed, else nil - edit_boxes[1]:GetInteger(), edit_boxes[2]:GetInteger() -end - -function layers_swap_selective() - local ok, layer_a, layer_b = choose_layers_to_swap() - if ok == finale.EXECMODAL_OK and not find_errors(layer_a, layer_b) then - layer.swap(finenv.Region(), layer_a, layer_b) - end -end - -layers_swap_selective() + ]]return"Swap layers selective","Swap layers selective","Swap layers selectively"end;local o=require("library.layer")function find_errors(p,q)local r=""if p<1 or p>4 or q<1 or q>4 then r="Layer numbers must be\nintegers between 1 and 4"elseif p==q then r="Please nominate two DIFFERENT layers"end;if r~=""then finenv.UI():AlertNeutral("(script: "..plugindef()..")",r)return true end;return false end;function choose_layers_to_swap()local s=10;local t=25;local u=finenv.UI():IsOnMac()and 3 or 0;local v=110;local w={}local string=finale.FCString()local x=finale.FCCustomWindow()string.LuaString=plugindef()x:SetTitle(string)local y={{"swap layer# (1-4):",1},{"with layer# (1-4):",2}}for z,A in ipairs(y)do string.LuaString=A[1]local B=x:CreateStatic(0,s)B:SetText(string)B:SetWidth(200)w[z]=x:CreateEdit(v,s-u)w[z]:SetInteger(A[2])s=s+t end;x:CreateOkButton()x:CreateCancelButton()return x:ExecuteModal(nil),w[1]:GetInteger(),w[2]:GetInteger()end;function layers_swap_selective()local C,p,q=choose_layers_to_swap()if C==finale.EXECMODAL_OK and not find_errors(p,q)then o.swap(finenv.Region(),p,q)end end;layers_swap_selective()end)c("library.layer",function(require,n,c,d)local D={}function D.finale_version(E,F,G)local H=bit32.bor(bit32.lshift(math.floor(E),24),bit32.lshift(math.floor(F),20))if G then H=bit32.bor(H,math.floor(G))end;return H end;function D.group_overlaps_region(I,J)if J:IsFullDocumentSpan()then return true end;local K=false;local L=finale.FCSystemStaves()L:LoadAllForRegion(J)for M in each(L)do if I:ContainsStaff(M:GetStaff())then K=true;break end end;if not K then return false end;if I.StartMeasure>J.EndMeasure or I.EndMeasure0 then T=true;break end;R=R+1 end;if T then local U=finale.FCStaffSystem()U:Load(S:GetFirstSystem())return finale.FCCell(U.FirstMeasure,U.TopStaff)end;local V=finale.FCMusicRegion()V:SetFullDocument()return finale.FCCell(V.EndMeasure,V.EndStaff)end;function D.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local W=finale.FCMusicRegion()W:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),W.StartStaff)end;return D.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function D.get_top_left_selected_or_visible_cell()local P=finenv.Region()if not P:IsEmpty()then return finale.FCCell(P.StartMeasure,P.StartStaff)end;return D.get_top_left_visible_cell()end;function D.is_default_measure_number_visible_on_cell(X,Y,Z,_)local a0=finale.FCCurrentStaffSpec()if not a0:LoadForCell(Y,0)then return false end;if X:GetShowOnTopStaff()and Y.Staff==Z.TopStaff then return true end;if X:GetShowOnBottomStaff()and Y.Staff==Z:CalcBottomStaff()then return true end;if a0.ShowMeasureNumbers then return not X:GetExcludeOtherStaves(_)end;return false end;function D.is_default_number_visible_and_left_aligned(X,Y,a1,_,a2)if X.UseScoreInfoForParts then _=false end;if a2 and X:GetShowOnMultiMeasureRests(_)then if finale.MNALIGN_LEFT~=X:GetMultiMeasureAlignment(_)then return false end elseif Y.Measure==a1.FirstMeasure then if not X:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=X:GetStartAlignment(_)then return false end else if not X:GetShowMultiples(_)then return false end;if finale.MNALIGN_LEFT~=X:GetMultipleAlignment(_)then return false end end;return D.is_default_measure_number_visible_on_cell(X,Y,a1,_)end;function D.update_layout(a3,a4)a3=a3 or 1;a4=a4 or false;local a5=finale.FCPage()if a5:Load(a3)then a5:UpdateLayout(a4)end end;function D.get_current_part()local a6=finale.FCParts()a6:LoadAll()return a6:GetCurrent()end;function D.get_page_format_prefs()local a7=D.get_current_part()local a8=finale.FCPageFormatPrefs()local a9=false;if a7:IsScore()then a9=a8:LoadScore()else a9=a8:LoadParts()end;return a8,a9 end;function D.get_smufl_metadata_file(aa)if not aa then aa=finale.FCFontInfo()aa:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local ab=function(ac,aa)local ad=ac.."/SMuFL/Fonts/"..aa.Name.."/"..aa.Name..".json"return io.open(ad,"r")end;local ae=""if finenv.UI():IsOnWindows()then ae=os.getenv("LOCALAPPDATA")else ae=os.getenv("HOME").."/Library/Application Support"end;local af=ab(ae,aa)if nil~=af then return af end;local ag="/Library/Application Support"if finenv.UI():IsOnWindows()then ag=os.getenv("COMMONPROGRAMFILES")end;return ab(ag,aa)end;function D.is_font_smufl_font(aa)if not aa then aa=finale.FCFontInfo()aa:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=D.finale_version(27,1)then if nil~=aa.IsSMuFLFont then return aa.IsSMuFLFont end end;local ah=D.get_smufl_metadata_file(aa)if nil~=ah then io.close(ah)return true end;return false end;function D.simple_input(ai,aj)local ak=finale.FCString()ak.LuaString=""local al=finale.FCString()local am=160;function format_ctrl(an,ao,ap,aq)an:SetHeight(ao)an:SetWidth(ap)al.LuaString=aq;an:SetText(al)end;title_width=string.len(ai)*6+54;if title_width>am then am=title_width end;text_width=string.len(aj)*6;if text_width>am then am=text_width end;al.LuaString=ai;local x=finale.FCCustomLuaWindow()x:SetTitle(al)local ar=x:CreateStatic(0,0)format_ctrl(ar,16,am,aj)local as=x:CreateEdit(0,20)format_ctrl(as,20,am,"")x:CreateOkButton()x:CreateCancelButton()function callback(an)end;x:RegisterHandleCommand(callback)if x:ExecuteModal(nil)==finale.EXECMODAL_OK then ak.LuaString=as:GetText(ak)return ak.LuaString end end;function D.is_finale_object(at)return at and type(at)=="userdata"and at.ClassName and at.GetClassID and true or false end;function D.system_indent_set_to_prefs(a1,a8)a8=a8 or D.get_page_format_prefs()local au=finale.FCMeasure()local av=a1.FirstMeasure==1;if not av and au:Load(a1.FirstMeasure)then if au.ShowFullNames then av=true end end;if av and a8.UseFirstSystemMargins then a1.LeftMargin=a8.FirstSystemLeft else a1.LeftMargin=a8.SystemLeft end;return a1:Save()end;return D end)return a("__root") \ No newline at end of file diff --git a/dist/lyrics_baseline_move_down.lua b/dist/lyrics_baseline_move_down.lua deleted file mode 100644 index b0cb338f..00000000 --- a/dist/lyrics_baseline_move_down.lua +++ /dev/null @@ -1,206 +0,0 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Version = "1.0" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "June 23, 2021" - finaleplugin.CategoryTags = "Lyric" - finaleplugin.AuthorURL = "http://robertgpatterson.com" - return "Move Lyric Baselines Down", "Move Lyrics Baselines Down", - "Moves all selected lyrics baselines down one space" -end - --- Author: Robert Patterson --- Date: March 5, 2021 ---[[ -$module Configuration - -This library implements a UTF-8 text file scheme for configuration as follows: - -- Comments start with `--` -- Leading, trailing, and extra whitespace is ignored -- Each parameter is named and delimited as follows: -` = ` - -Parameter values may be: - -- Strings delimited with either single- or double-quotes -- Tables delimited with `{}` that may contain strings, booleans, or numbers -- Booleans (`true` or `false`) -- Numbers - -Currently the following are not supported: - -- Tables embedded within tables -- Tables containing strings that contain commas - -A sample configuration file might be: - -```lua --- Configuration File for "Hairpin and Dynamic Adjustments" script --- -left_dynamic_cushion = 12 --evpus -right_dynamic_cushion = -6 --evpus -``` - -Configuration files must be placed in a subfolder called `script_settings` within -the folder of the calling script. Each script that has a configuration file -defines its own configuration file name. -]] -- -local configuration = {} - -local script_settings_dir = "script_settings" -- the parent of this directory is the running lua path -local comment_marker = "--" -local parameter_delimiter = "=" -local path_delimiter = "/" - -local file_exists = function(file_path) - local f = io.open(file_path, "r") - if nil ~= f then - io.close(f) - return true - end - return false -end - -local strip_leading_trailing_whitespace = function(str) - return str:match("^%s*(.-)%s*$") -- lua pattern magic taken from the Internet -end - -local parse_parameter -- forward function declaration - -local parse_table = function(val_string) - local ret_table = {} - for element in val_string:gmatch("[^,%s]+") do -- lua pattern magic taken from the Internet - local parsed_element = parse_parameter(element) - table.insert(ret_table, parsed_element) - end - return ret_table -end - -parse_parameter = function(val_string) - if "\"" == val_string:sub(1, 1) and "\"" == val_string:sub(#val_string, #val_string) then -- double-quote string - return string.gsub(val_string, "\"(.+)\"", "%1") -- lua pattern magic: "(.+)" matches all characters between two double-quote marks (no escape chars) - elseif "'" == val_string:sub(1, 1) and "'" == val_string:sub(#val_string, #val_string) then -- single-quote string - return string.gsub(val_string, "'(.+)'", "%1") -- lua pattern magic: '(.+)' matches all characters between two single-quote marks (no escape chars) - elseif "{" == val_string:sub(1, 1) and "}" == val_string:sub(#val_string, #val_string) then - return parse_table(string.gsub(val_string, "{(.+)}", "%1")) - elseif "true" == val_string then - return true - elseif "false" == val_string then - return false - end - return tonumber(val_string) -end - -local get_parameters_from_file = function(file_name) - local parameters = {} - - local path = finale.FCString() - path:SetRunningLuaFolderPath() - local file_path = path.LuaString .. path_delimiter .. file_name - if not file_exists(file_path) then - return parameters - end - - for line in io.lines(file_path) do - local comment_at = string.find(line, comment_marker, 1, true) -- true means find raw string rather than lua pattern - if nil ~= comment_at then - line = string.sub(line, 1, comment_at - 1) - end - local delimiter_at = string.find(line, parameter_delimiter, 1, true) - if nil ~= delimiter_at then - local name = strip_leading_trailing_whitespace(string.sub(line, 1, delimiter_at - 1)) - local val_string = strip_leading_trailing_whitespace(string.sub(line, delimiter_at + 1)) - parameters[name] = parse_parameter(val_string) - end - end - - return parameters -end - ---[[ -% get_parameters - -Searches for a file with the input filename in the `script_settings` directory and replaces the default values in `parameter_list` with any that are found in the config file. - -@ file_name (string) the file name of the config file (which will be prepended with the `script_settings` directory) -@ parameter_list (table) a table with the parameter name as key and the default value as value -]] -function configuration.get_parameters(file_name, parameter_list) - local file_parameters = get_parameters_from_file(script_settings_dir .. path_delimiter .. file_name) - if nil ~= file_parameters then - for param_name, def_val in pairs(parameter_list) do - local param_val = file_parameters[param_name] - if nil ~= param_val then - parameter_list[param_name] = param_val - end - end - end -end - - - - -local config = { - nudge_down_evpus = 24 -} - -if nil ~= configuration then - configuration.get_parameters("lyrics_baseline_move.config.txt", config) -end - -local baseline_types = { - [finale.BASELINEMODE_LYRICSVERSE] = function() return finale.FCVerseLyricsText() end, - [finale.BASELINEMODE_LYRICSCHORUS] = function() return finale.FCChorusLyricsText() end, - [finale.BASELINEMODE_LYRICSSECTION] = function() return finale.FCSectionLyricsText() end -} - -function find_valid_lyric_nums() - local valid_lyric_nums = {} - for baseline_type, lyrics_text_class_constructor in pairs(baseline_types) do - local lyrics_text_class = lyrics_text_class_constructor() - for i = 1, 32767, 1 do - if lyrics_text_class:Load(i) then - local str = finale.FCString() - lyrics_text_class:GetText(str) - if not str:IsEmpty() then - valid_lyric_nums[{baseline_type, i}] = 1 - end - end - end - end - return valid_lyric_nums -end - -function lyrics_baseline_move_down() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - local valid_lyric_nums = find_valid_lyric_nums() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - for lyric_info, _ in pairs(valid_lyric_nums) do - local baseline_type, lyric_number = table.unpack(lyric_info) - baselines:LoadAllForSystem(baseline_type, i) - for j = start_slot, end_slot do - bl = baselines:AssureSavedLyricNumber(baseline_type, i, region:CalcStaffNumber(j), lyric_number) - bl.VerticalOffset = bl.VerticalOffset - config.nudge_down_evpus - bl:Save() - end - end - end -end - -lyrics_baseline_move_down() diff --git a/dist/lyrics_baseline_move_up.lua b/dist/lyrics_baseline_move_up.lua deleted file mode 100644 index 12780d22..00000000 --- a/dist/lyrics_baseline_move_up.lua +++ /dev/null @@ -1,206 +0,0 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Version = "1.0" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "June 23, 2021" - finaleplugin.CategoryTags = "Lyric" - finaleplugin.AuthorURL = "http://robertgpatterson.com" - return "Move Lyric Baselines Up", "Move Lyrics Baselines Up", - "Moves all selected lyrics baselines up one space" -end - --- Author: Robert Patterson --- Date: March 5, 2021 ---[[ -$module Configuration - -This library implements a UTF-8 text file scheme for configuration as follows: - -- Comments start with `--` -- Leading, trailing, and extra whitespace is ignored -- Each parameter is named and delimited as follows: -` = ` - -Parameter values may be: - -- Strings delimited with either single- or double-quotes -- Tables delimited with `{}` that may contain strings, booleans, or numbers -- Booleans (`true` or `false`) -- Numbers - -Currently the following are not supported: - -- Tables embedded within tables -- Tables containing strings that contain commas - -A sample configuration file might be: - -```lua --- Configuration File for "Hairpin and Dynamic Adjustments" script --- -left_dynamic_cushion = 12 --evpus -right_dynamic_cushion = -6 --evpus -``` - -Configuration files must be placed in a subfolder called `script_settings` within -the folder of the calling script. Each script that has a configuration file -defines its own configuration file name. -]] -- -local configuration = {} - -local script_settings_dir = "script_settings" -- the parent of this directory is the running lua path -local comment_marker = "--" -local parameter_delimiter = "=" -local path_delimiter = "/" - -local file_exists = function(file_path) - local f = io.open(file_path, "r") - if nil ~= f then - io.close(f) - return true - end - return false -end - -local strip_leading_trailing_whitespace = function(str) - return str:match("^%s*(.-)%s*$") -- lua pattern magic taken from the Internet -end - -local parse_parameter -- forward function declaration - -local parse_table = function(val_string) - local ret_table = {} - for element in val_string:gmatch("[^,%s]+") do -- lua pattern magic taken from the Internet - local parsed_element = parse_parameter(element) - table.insert(ret_table, parsed_element) - end - return ret_table -end - -parse_parameter = function(val_string) - if "\"" == val_string:sub(1, 1) and "\"" == val_string:sub(#val_string, #val_string) then -- double-quote string - return string.gsub(val_string, "\"(.+)\"", "%1") -- lua pattern magic: "(.+)" matches all characters between two double-quote marks (no escape chars) - elseif "'" == val_string:sub(1, 1) and "'" == val_string:sub(#val_string, #val_string) then -- single-quote string - return string.gsub(val_string, "'(.+)'", "%1") -- lua pattern magic: '(.+)' matches all characters between two single-quote marks (no escape chars) - elseif "{" == val_string:sub(1, 1) and "}" == val_string:sub(#val_string, #val_string) then - return parse_table(string.gsub(val_string, "{(.+)}", "%1")) - elseif "true" == val_string then - return true - elseif "false" == val_string then - return false - end - return tonumber(val_string) -end - -local get_parameters_from_file = function(file_name) - local parameters = {} - - local path = finale.FCString() - path:SetRunningLuaFolderPath() - local file_path = path.LuaString .. path_delimiter .. file_name - if not file_exists(file_path) then - return parameters - end - - for line in io.lines(file_path) do - local comment_at = string.find(line, comment_marker, 1, true) -- true means find raw string rather than lua pattern - if nil ~= comment_at then - line = string.sub(line, 1, comment_at - 1) - end - local delimiter_at = string.find(line, parameter_delimiter, 1, true) - if nil ~= delimiter_at then - local name = strip_leading_trailing_whitespace(string.sub(line, 1, delimiter_at - 1)) - local val_string = strip_leading_trailing_whitespace(string.sub(line, delimiter_at + 1)) - parameters[name] = parse_parameter(val_string) - end - end - - return parameters -end - ---[[ -% get_parameters - -Searches for a file with the input filename in the `script_settings` directory and replaces the default values in `parameter_list` with any that are found in the config file. - -@ file_name (string) the file name of the config file (which will be prepended with the `script_settings` directory) -@ parameter_list (table) a table with the parameter name as key and the default value as value -]] -function configuration.get_parameters(file_name, parameter_list) - local file_parameters = get_parameters_from_file(script_settings_dir .. path_delimiter .. file_name) - if nil ~= file_parameters then - for param_name, def_val in pairs(parameter_list) do - local param_val = file_parameters[param_name] - if nil ~= param_val then - parameter_list[param_name] = param_val - end - end - end -end - - - - -local config = { - nudge_up_evpus = 24 -} - -if nil ~= configuration then - configuration.get_parameters("lyrics_baseline_move.config.txt", config) -end - -local baseline_types = { - [finale.BASELINEMODE_LYRICSVERSE] = function() return finale.FCVerseLyricsText() end, - [finale.BASELINEMODE_LYRICSCHORUS] = function() return finale.FCChorusLyricsText() end, - [finale.BASELINEMODE_LYRICSSECTION] = function() return finale.FCSectionLyricsText() end -} - -function find_valid_lyric_nums() - local valid_lyric_nums = {} - for baseline_type, lyrics_text_class_constructor in pairs(baseline_types) do - local lyrics_text_class = lyrics_text_class_constructor() - for i = 1, 32767, 1 do - if lyrics_text_class:Load(i) then - local str = finale.FCString() - lyrics_text_class:GetText(str) - if not str:IsEmpty() then - valid_lyric_nums[{baseline_type, i}] = 1 - end - end - end - end - return valid_lyric_nums -end - -function lyrics_baseline_move_up() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - local valid_lyric_nums = find_valid_lyric_nums() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - for lyric_info, _ in pairs(valid_lyric_nums) do - local baseline_type, lyric_number = table.unpack(lyric_info) - baselines:LoadAllForSystem(baseline_type, i) - for j = start_slot, end_slot do - bl = baselines:AssureSavedLyricNumber(baseline_type, i, region:CalcStaffNumber(j), lyric_number) - bl.VerticalOffset = bl.VerticalOffset + config.nudge_up_evpus - bl:Save() - end - end - end -end - -lyrics_baseline_move_up() diff --git a/dist/lyrics_baseline_reset.lua b/dist/lyrics_baseline_reset.lua deleted file mode 100644 index 64e6dffc..00000000 --- a/dist/lyrics_baseline_reset.lua +++ /dev/null @@ -1,47 +0,0 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Version = "1.0" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Date = "July 7, 2021" - finaleplugin.CategoryTags = "Lyric" - finaleplugin.AuthorURL = "http://robertgpatterson.com" - return "Reset Lyric Baselines", "Reset Lyrics Baselines", - "Resets all selected lyrics baselines to default" -end - -local baseline_types = { - finale.BASELINEMODE_LYRICSVERSE, - finale.BASELINEMODE_LYRICSCHORUS, - finale.BASELINEMODE_LYRICSSECTION -} - -function lyrics_baseline_reset() - local region = finenv.Region() - local systems = finale.FCStaffSystems() - systems:LoadAll() - - local start_measure = region:GetStartMeasure() - local end_measure = region:GetEndMeasure() - local system = systems:FindMeasureNumber(start_measure) - local lastSys = systems:FindMeasureNumber(end_measure) - local system_number = system:GetItemNo() - local lastSys_number = lastSys:GetItemNo() - local start_slot = region:GetStartSlot() - local end_slot = region:GetEndSlot() - - for i = system_number, lastSys_number, 1 do - local baselines = finale.FCBaselines() - for _, baseline_type in pairs(baseline_types) do - baselines:LoadAllForSystem(baseline_type, i) - for baseline in each(baselines) do - local baseline_slot = region:CalcSlotNumber(baseline.Staff) - if (start_slot <= baseline_slot) and (baseline_slot <= end_slot) then - baseline:DeleteData() - end - end - end - end -end - -lyrics_baseline_reset() diff --git a/dist/measure_force_full_names.lua b/dist/measure_force_full_names.lua index 5bf78bd5..f71f2f0b 100644 --- a/dist/measure_force_full_names.lua +++ b/dist/measure_force_full_names.lua @@ -1,41 +1 @@ -function plugindef() - -- This function and the 'finaleplugin' namespace - -- are both reserved for the plug-in definition. - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0" - finaleplugin.Date = "June 10, 2020" - finaleplugin.CategoryTags = "Measure" - return "Force Full Names", "Force Full Names", "Force first selected measure to show full staff names." -end - ---NOTE: This script replaces a pre-Finale 2011 Patterson Plugin that created staff styles and massaged group names ---to achieve a result that looked like the full names on the first system. But starting in Fin11 all we have to do is ---set a measure option. The only reason to use this script any more is that it may not require as much motor precision ---as opening the meas dialog and clicking an option. - -function measure_force_full_names() - - local systems = finale.FCStaffSystems() - systems:LoadAll() - local measure_number = finenv.Region().StartMeasure - local system = systems:FindMeasureNumber(measure_number) - if measure_number ~= system.FirstMeasure then - if finale.OKRETURN ~= finenv.UI():AlertOkCancel("The first measure you selected is not at the beginning of a system. Do you wish to process the first measure of this system?", nil) then - return; - end - measure_number = system.FirstMeasure - end - - local measure = finale.FCMeasure() - if not measure:Load(measure_number) then - finenv.UI():AlertInfo("Unable to find measure " .. measure_number) - return - end - measure.ShowFullNames = true; - measure.SystemBreak = true; --set this because Finale does so automatically when you set "show full names" in the measure attributes dialog - measure:Save() -end - -measure_force_full_names() \ No newline at end of file +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Robert Patterson"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0"finaleplugin.Date="June 10, 2020"finaleplugin.CategoryTags="Measure"return"Force Full Names","Force Full Names","Force first selected measure to show full staff names."end;function measure_force_full_names()local a=finale.FCStaffSystems()a:LoadAll()local b=finenv.Region().StartMeasure;local c=a:FindMeasureNumber(b)if b~=c.FirstMeasure then if finale.OKRETURN~=finenv.UI():AlertOkCancel("The first measure you selected is not at the beginning of a system. Do you wish to process the first measure of this system?",nil)then return end;b=c.FirstMeasure end;local d=finale.FCMeasure()if not d:Load(b)then finenv.UI():AlertInfo("Unable to find measure "..b)return end;d.ShowFullNames=true;d.SystemBreak=true;d:Save()end;measure_force_full_names() \ No newline at end of file diff --git a/dist/meter_change.lua b/dist/meter_change.lua index ba814d72..e95ad4bb 100644 --- a/dist/meter_change.lua +++ b/dist/meter_change.lua @@ -1,13 +1,4 @@ -function plugindef() - finaleplugin.Author = "Nick Mazuk" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0.0" - finaleplugin.Date = "June 4, 2022" - finaleplugin.CategoryTags = "Meter" - finaleplugin.MinJWLuaVersion = 0.62 - finaleplugin.AuthorURL = "https://nickmazuk.com" - finaleplugin.RequireSelection = true - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.Author="Nick Mazuk"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0.0"finaleplugin.Date="June 4, 2022"finaleplugin.CategoryTags="Meter"finaleplugin.MinJWLuaVersion=0.62;finaleplugin.AuthorURL="https://nickmazuk.com"finaleplugin.RequireSelection=true;finaleplugin.Notes=[[ Changes the meter in a selected range. If a single measure is selected, @@ -28,8 +19,7 @@ function plugindef() If multiple measures are selected, the meter will be set exactly for the selected measures. - ]] - finaleplugin.AdditionalMenuOptions = [[ + ]]finaleplugin.AdditionalMenuOptions=[[ Meter - 1/2 Meter - 2/2 Meter - 3/2 @@ -51,8 +41,7 @@ function plugindef() Meter - 9/8 Meter - 12/8 Meter - 15/8 - ]] - finaleplugin.AdditionalPrefixes = [[ + ]]finaleplugin.AdditionalPrefixes=[[ numerator = 1 denominator = 2 numerator = 2 denominator = 2 numerator = 3 denominator = 2 @@ -74,369 +63,4 @@ function plugindef() numerator = 9 denominator = 8 numerator = 12 denominator = 8 numerator = 15 denominator = 8 - ]] - return "Meter - 4/4", "Meter - 4/4", "Sets the meter as indicated in a selected range." -end - --- Author: Robert Patterson --- Date: March 5, 2021 ---[[ -$module Configuration - -This library implements a UTF-8 text file scheme for configuration and user settings as follows: - -- Comments start with `--` -- Leading, trailing, and extra whitespace is ignored -- Each parameter is named and delimited as follows: - -``` - = -``` - -Parameter values may be: - -- Strings delimited with either single- or double-quotes -- Tables delimited with `{}` that may contain strings, booleans, or numbers -- Booleans (`true` or `false`) -- Numbers - -Currently the following are not supported: - -- Tables embedded within tables -- Tables containing strings that contain commas - -A sample configuration file might be: - -```lua --- Configuration File for "Hairpin and Dynamic Adjustments" script --- -left_dynamic_cushion = 12 --evpus -right_dynamic_cushion = -6 --evpus -``` - -## Configuration Files - -Configuration files provide a way for power users to modify script behavior without -having to modify the script itself. Some users track their changes to their configuration files, -so scripts should not create or modify them programmatically. - -- The user creates each configuration file in a subfolder called `script_settings` within -the folder of the calling script. -- Each script that has a configuration file defines its own configuration file name. -- It is entirely appropriate over time for scripts to transition from configuration files to user settings, -but this requires implementing a user interface to modify the user settings from within the script. -(See below.) - -## User Settings Files - -User settings are written by the scripts themselves and reside in the user's preferences folder -in an appropriately-named location for the operating system. (The naming convention is a detail that the -configuration library handles for the caller.) If the user settings are to be changed from their defaults, -the script itself should provide a means to change them. This could be a (preferably optional) dialog box -or any other mechanism the script author chooses. - -User settings are saved in the user's preferences folder (on Mac) or AppData folder (on Windows). - -## Merge Process - -Files are _merged_ into the passed-in list of default values. They do not _replace_ the list. Each calling script contains -a table of all the configurable parameters or settings it recognizes along with default values. An example: - -`sample.lua:` - -```lua -parameters = { - x = 1, - y = 2, - z = 3 -} - -configuration.get_parameters(parameters, "script.config.txt") - -for k, v in pairs(parameters) do - print(k, v) -end -``` - -Suppose the `script.config.text` file is as follows: - -``` -y = 4 -q = 6 -``` - -The returned parameters list is: - - -```lua -parameters = { - x = 1, -- remains the default value passed in - y = 4, -- replaced value from the config file - z = 3 -- remains the default value passed in -} -``` - -The `q` parameter in the config file is ignored because the input paramater list -had no `q` parameter. - -This approach allows total flexibility for the script add to or modify its list of parameters -without having to worry about older configuration files or user settings affecting it. -]] - -local configuration = {} - -local script_settings_dir = "script_settings" -- the parent of this directory is the running lua path -local comment_marker = "--" -local parameter_delimiter = "=" -local path_delimiter = "/" - -local file_exists = function(file_path) - local f = io.open(file_path, "r") - if nil ~= f then - io.close(f) - return true - end - return false -end - -local strip_leading_trailing_whitespace = function(str) - return str:match("^%s*(.-)%s*$") -- lua pattern magic taken from the Internet -end - -local parse_table = function(val_string) - local ret_table = {} - for element in val_string:gmatch("[^,%s]+") do -- lua pattern magic taken from the Internet - local parsed_element = parse_parameter(element) - table.insert(ret_table, parsed_element) - end - return ret_table -end - -parse_parameter = function(val_string) - if "\"" == val_string:sub(1, 1) and "\"" == val_string:sub(#val_string, #val_string) then -- double-quote string - return string.gsub(val_string, "\"(.+)\"", "%1") -- lua pattern magic: "(.+)" matches all characters between two double-quote marks (no escape chars) - elseif "'" == val_string:sub(1, 1) and "'" == val_string:sub(#val_string, #val_string) then -- single-quote string - return string.gsub(val_string, "'(.+)'", "%1") -- lua pattern magic: '(.+)' matches all characters between two single-quote marks (no escape chars) - elseif "{" == val_string:sub(1, 1) and "}" == val_string:sub(#val_string, #val_string) then - return parse_table(string.gsub(val_string, "{(.+)}", "%1")) - elseif "true" == val_string then - return true - elseif "false" == val_string then - return false - end - return tonumber(val_string) -end - -local get_parameters_from_file = function(file_path, parameter_list) - local file_parameters = {} - - if not file_exists(file_path) then - return false - end - - for line in io.lines(file_path) do - local comment_at = string.find(line, comment_marker, 1, true) -- true means find raw string rather than lua pattern - if nil ~= comment_at then - line = string.sub(line, 1, comment_at - 1) - end - local delimiter_at = string.find(line, parameter_delimiter, 1, true) - if nil ~= delimiter_at then - local name = strip_leading_trailing_whitespace(string.sub(line, 1, delimiter_at - 1)) - local val_string = strip_leading_trailing_whitespace(string.sub(line, delimiter_at + 1)) - file_parameters[name] = parse_parameter(val_string) - end - end - - for param_name, _ in pairs(parameter_list) do - local param_val = file_parameters[param_name] - if nil ~= param_val then - parameter_list[param_name] = param_val - end - end - - return true -end - ---[[ -% get_parameters - -Searches for a file with the input filename in the `script_settings` directory and replaces the default values in `parameter_list` -with any that are found in the config file. - -@ file_name (string) the file name of the config file (which will be prepended with the `script_settings` directory) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true if the file exists -]] -function configuration.get_parameters(file_name, parameter_list) - local path = "" - if finenv.IsRGPLua then - path = finenv.RunningLuaFolderPath() - else - local str = finale.FCString() - str:SetRunningLuaFolderPath() - path = str.LuaString - end - local file_path = path .. script_settings_dir .. path_delimiter .. file_name - return get_parameters_from_file(file_path, parameter_list) -end - --- Calculates a filepath in the user's preferences folder using recommended naming conventions --- -local calc_preferences_filepath = function(script_name) - local str = finale.FCString() - str:SetUserOptionsPath() - local folder_name = str.LuaString - if not finenv.IsRGPLua and finenv.UI():IsOnMac() then - -- works around bug in SetUserOptionsPath() in JW Lua - folder_name = os.getenv("HOME") .. folder_name:sub(2) -- strip '~' and replace with actual folder - end - if finenv.UI():IsOnWindows() then - folder_name = folder_name .. path_delimiter .. "FinaleLua" - end - local file_path = folder_name .. path_delimiter - if finenv.UI():IsOnMac() then - file_path = file_path .. "com.finalelua." - end - file_path = file_path .. script_name .. ".settings.txt" - return file_path, folder_name -end - ---[[ -% save_user_settings - -Saves the user's preferences for a script from the values provided in `parameter_list`. - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -: (boolean) true on success -]] -function configuration.save_user_settings(script_name, parameter_list) - local file_path, folder_path = calc_preferences_filepath(script_name) - local file = io.open(file_path, "w") - if not file and finenv.UI():IsOnWindows() then -- file not found - os.execute('mkdir "' .. folder_path ..'"') -- so try to make a folder (windows only, since the folder is guaranteed to exist on mac) - file = io.open(file_path, "w") -- try the file again - end - if not file then -- still couldn't find file - return false -- so give up - end - file:write("-- User settings for " .. script_name .. ".lua\n\n") - for k,v in pairs(parameter_list) do -- only number, boolean, or string values - if type(v) == "string" then - v = "\"" .. v .."\"" - else - v = tostring(v) - end - file:write(k, " = ", v, "\n") - end - file:close() - return true -- success -end - ---[[ -% get_user_settings - -Find the user's settings for a script in the preferences directory and replaces the default values in `parameter_list` -with any that are found in the preferences file. The actual name and path of the preferences file is OS dependent, so -the input string should just be the script name (without an extension). - -@ script_name (string) the name of the script (without an extension) -@ parameter_list (table) a table with the parameter name as key and the default value as value -@ [create_automatically] (boolean) if true, create the file automatically (default is `true`) -: (boolean) `true` if the file already existed, `false` if it did not or if it was created automatically -]] -function configuration.get_user_settings(script_name, parameter_list, create_automatically) - if create_automatically == nil then create_automatically = true end - local exists = get_parameters_from_file(calc_preferences_filepath(script_name), parameter_list) - if not exists and create_automatically then - configuration.save_user_settings(script_name, parameter_list) - end - return exists -end - - - - -config = -{ - stop_at_always_show = true -} -configuration.get_parameters("meter_change.config.txt", config) - - -numerator = numerator or 4 -denominator = denominator or 4 -composite = composite or nil -if denominator == 8 and not composite then - numerator = numerator / 3 -end -num_composite = 0 -if composite then - for k, v in pairs(composite) do - num_composite = num_composite + 1 - end -end - -local denominators = {} -denominators[2] = 2048 -denominators[4] = 1024 -denominators[8] = composite and 512 or 1536 -- for compound meters - -local do_single_bar = finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_ALT) or finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_SHIFT) - -function apply_new_time(measure, beat_num, beat_duration) - local time_sig = measure:GetTimeSignature() - if composite then - local top_list = finale.FCCompositeTimeSigTop() - top_list:AddGroup(num_composite) - for k, v in ipairs(composite) do - top_list:SetGroupElementBeats(0, k-1, v) - end - time_sig:SaveNewCompositeTop(top_list) - measure.UseTimeSigForDisplay = true - local abrv_time_sig = measure:GetTimeSignatureForDisplay() - abrv_time_sig:RemoveCompositeTop(beat_num) - abrv_time_sig:RemoveCompositeBottom(beat_duration) - else - if measure.UseTimeSigForDisplay then - local abrv_time_sig = measure:GetTimeSignatureForDisplay() - abrv_time_sig:RemoveCompositeTop(beat_num) - abrv_time_sig:RemoveCompositeBottom(beat_duration) - measure.UseTimeSigForDisplay = false - end - time_sig:RemoveCompositeTop(beat_num) - end - time_sig:RemoveCompositeBottom(beat_duration) -end - -function set_time(beat_num, beat_duration) - local measures = finale.FCMeasures() - measures:LoadRegion(finenv.Region()) - if measures.Count > 1 or do_single_bar then - for m in each(measures) do - apply_new_time(m, beat_num, beat_duration) - m:Save() - end - else - local selected_measure = measures:GetItemAt(0) - local selected_time_signature = selected_measure:GetTimeSignature() - -- Do the selected measure last in case it is a composite time sig. - -- We have to preserve the composite time sig record for it so that comparisons with selected_time_signature work. - for m in loadall(finale.FCMeasures()) do - if (m.ItemNo > selected_measure.ItemNo) then - if config.stop_at_always_show and m.ShowTimeSignature == finale.SHOWSTATE_SHOW then - break - end - if not selected_time_signature:IsIdentical(m:GetTimeSignature()) then - break - end - apply_new_time(m, beat_num, beat_duration) - m:Save() - end - end - apply_new_time(selected_measure, beat_num, beat_duration) - selected_measure:Save() - end -end - -set_time(numerator, denominators[denominator]) + ]]return"Meter - 4/4","Meter - 4/4","Sets the meter as indicated in a selected range."end;local o=require("library.configuration")config={stop_at_always_show=true}o.get_parameters("meter_change.config.txt",config)numerator=numerator or 4;denominator=denominator or 4;composite=composite or nil;if denominator==8 and not composite then numerator=numerator/3 end;num_composite=0;if composite then for p,q in pairs(composite)do num_composite=num_composite+1 end end;local r={}r[2]=2048;r[4]=1024;r[8]=composite and 512 or 1536;local s=finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_ALT)or finenv.QueryInvokedModifierKeys(finale.CMDMODKEY_SHIFT)function apply_new_time(t,u,v)local w=t:GetTimeSignature()if composite then local x=finale.FCCompositeTimeSigTop()x:AddGroup(num_composite)for p,q in ipairs(composite)do x:SetGroupElementBeats(0,p-1,q)end;w:SaveNewCompositeTop(x)t.UseTimeSigForDisplay=true;local y=t:GetTimeSignatureForDisplay()y:RemoveCompositeTop(u)y:RemoveCompositeBottom(v)else if t.UseTimeSigForDisplay then local y=t:GetTimeSignatureForDisplay()y:RemoveCompositeTop(u)y:RemoveCompositeBottom(v)t.UseTimeSigForDisplay=false end;w:RemoveCompositeTop(u)end;w:RemoveCompositeBottom(v)end;function set_time(u,v)local z=finale.FCMeasures()z:LoadRegion(finenv.Region())if z.Count>1 or s then for A in each(z)do apply_new_time(A,u,v)A:Save()end else local B=z:GetItemAt(0)local C=B:GetTimeSignature()for A in loadall(finale.FCMeasures())do if A.ItemNo>B.ItemNo then if config.stop_at_always_show and A.ShowTimeSignature==finale.SHOWSTATE_SHOW then break end;if not C:IsIdentical(A:GetTimeSignature())then break end;apply_new_time(A,u,v)A:Save()end end;apply_new_time(B,u,v)B:Save()end end;set_time(numerator,r[denominator])end)c("library.configuration",function(require,n,c,d)local D={}function D.finale_version(E,F,G)local H=bit32.bor(bit32.lshift(math.floor(E),24),bit32.lshift(math.floor(F),20))if G then H=bit32.bor(H,math.floor(G))end;return H end;function D.group_overlaps_region(I,J)if J:IsFullDocumentSpan()then return true end;local K=false;local L=finale.FCSystemStaves()L:LoadAllForRegion(J)for M in each(L)do if I:ContainsStaff(M:GetStaff())then K=true;break end end;if not K then return false end;if I.StartMeasure>J.EndMeasure or I.EndMeasure0 then T=true;break end;R=R+1 end;if T then local U=finale.FCStaffSystem()U:Load(S:GetFirstSystem())return finale.FCCell(U.FirstMeasure,U.TopStaff)end;local V=finale.FCMusicRegion()V:SetFullDocument()return finale.FCCell(V.EndMeasure,V.EndStaff)end;function D.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local W=finale.FCMusicRegion()W:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),W.StartStaff)end;return D.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function D.get_top_left_selected_or_visible_cell()local P=finenv.Region()if not P:IsEmpty()then return finale.FCCell(P.StartMeasure,P.StartStaff)end;return D.get_top_left_visible_cell()end;function D.is_default_measure_number_visible_on_cell(X,Y,Z,_)local a0=finale.FCCurrentStaffSpec()if not a0:LoadForCell(Y,0)then return false end;if X:GetShowOnTopStaff()and Y.Staff==Z.TopStaff then return true end;if X:GetShowOnBottomStaff()and Y.Staff==Z:CalcBottomStaff()then return true end;if a0.ShowMeasureNumbers then return not X:GetExcludeOtherStaves(_)end;return false end;function D.is_default_number_visible_and_left_aligned(X,Y,a1,_,a2)if X.UseScoreInfoForParts then _=false end;if a2 and X:GetShowOnMultiMeasureRests(_)then if finale.MNALIGN_LEFT~=X:GetMultiMeasureAlignment(_)then return false end elseif Y.Measure==a1.FirstMeasure then if not X:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=X:GetStartAlignment(_)then return false end else if not X:GetShowMultiples(_)then return false end;if finale.MNALIGN_LEFT~=X:GetMultipleAlignment(_)then return false end end;return D.is_default_measure_number_visible_on_cell(X,Y,a1,_)end;function D.update_layout(a3,a4)a3=a3 or 1;a4=a4 or false;local a5=finale.FCPage()if a5:Load(a3)then a5:UpdateLayout(a4)end end;function D.get_current_part()local a6=finale.FCParts()a6:LoadAll()return a6:GetCurrent()end;function D.get_page_format_prefs()local a7=D.get_current_part()local a8=finale.FCPageFormatPrefs()local a9=false;if a7:IsScore()then a9=a8:LoadScore()else a9=a8:LoadParts()end;return a8,a9 end;function D.get_smufl_metadata_file(aa)if not aa then aa=finale.FCFontInfo()aa:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local ab=function(ac,aa)local ad=ac.."/SMuFL/Fonts/"..aa.Name.."/"..aa.Name..".json"return io.open(ad,"r")end;local ae=""if finenv.UI():IsOnWindows()then ae=os.getenv("LOCALAPPDATA")else ae=os.getenv("HOME").."/Library/Application Support"end;local af=ab(ae,aa)if nil~=af then return af end;local ag="/Library/Application Support"if finenv.UI():IsOnWindows()then ag=os.getenv("COMMONPROGRAMFILES")end;return ab(ag,aa)end;function D.is_font_smufl_font(aa)if not aa then aa=finale.FCFontInfo()aa:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=D.finale_version(27,1)then if nil~=aa.IsSMuFLFont then return aa.IsSMuFLFont end end;local ah=D.get_smufl_metadata_file(aa)if nil~=ah then io.close(ah)return true end;return false end;function D.simple_input(ai,aj)local ak=finale.FCString()ak.LuaString=""local al=finale.FCString()local am=160;function format_ctrl(an,ao,ap,aq)an:SetHeight(ao)an:SetWidth(ap)al.LuaString=aq;an:SetText(al)end;title_width=string.len(ai)*6+54;if title_width>am then am=title_width end;text_width=string.len(aj)*6;if text_width>am then am=text_width end;al.LuaString=ai;local ar=finale.FCCustomLuaWindow()ar:SetTitle(al)local as=ar:CreateStatic(0,0)format_ctrl(as,16,am,aj)local at=ar:CreateEdit(0,20)format_ctrl(at,20,am,"")ar:CreateOkButton()ar:CreateCancelButton()function callback(an)end;ar:RegisterHandleCommand(callback)if ar:ExecuteModal(nil)==finale.EXECMODAL_OK then ak.LuaString=at:GetText(ak)return ak.LuaString end end;function D.is_finale_object(au)return au and type(au)=="userdata"and au.ClassName and au.GetClassID and true or false end;function D.system_indent_set_to_prefs(a1,a8)a8=a8 or D.get_page_format_prefs()local av=finale.FCMeasure()local aw=a1.FirstMeasure==1;if not aw and av:Load(a1.FirstMeasure)then if av.ShowFullNames then aw=true end end;if aw and a8.UseFirstSystemMargins then a1.LeftMargin=a8.FirstSystemLeft else a1.LeftMargin=a8.SystemLeft end;return a1:Save()end;return D end)return a("__root") \ No newline at end of file diff --git a/dist/meter_set_numeric.lua b/dist/meter_set_numeric.lua index f8494486..5e5d6843 100644 --- a/dist/meter_set_numeric.lua +++ b/dist/meter_set_numeric.lua @@ -1,12 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.MinJWLuaVersion = 0.60 - finaleplugin.Author = "Carl Vine" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "0.57" - finaleplugin.Date = "2022/05/22" - finaleplugin.AuthorURL = "http://carlvine.com" - finaleplugin.Notes = [[ +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.MinJWLuaVersion=0.60;finaleplugin.Author="Carl Vine"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="0.57"finaleplugin.Date="2022/05/22"finaleplugin.AuthorURL="http://carlvine.com"finaleplugin.Notes=[[ This script requires RGPLua 0.60 or later and does not work under JWLua. (see: https://robertgpatterson.com/-fininfo/-rgplua/rgplua.html) @@ -17,359 +9,4 @@ All measures in the currently selected region will be assigned the new time sign "Bottom" numbers (denominators) are the usual "note" numbers - 2, 4, 8, 16, 32 or 64. "Top" numbers (numerators) must be integers, optionally joined by '+' signs. Multiples of 3 will automatically convert to compound signatures so, for instance, (9/16) will convert to three groups of dotted 8ths. To suppress automatic compounding, instead of the bottom 'note' number enter its EDU equivalent (quarter note = 1024; eighth note = 512 etc) but be careful since Finale can get confused if the number is inappropriate. Empty and zero "Top" numbers are ignored. If the "Secondary" Top is zero, "Tertiary" values are ignored. Finale's Time Signature tool will also accept "Top" numbers with decimals but I haven't allowed for that in this script. -]] - return "Meter Set Numeric", "Meter Set Numeric", "Set the Meter Numerically" -end - -function user_chooses_meter(meters, region) - local horiz = { 0, 70, 130, 210, 290, 300 } -- dialog items grid - local vert = { 0, 20, 40, 60, 82, 115, 135, 155, 175 } - local start = region.StartMeasure - local stop = region.EndMeasure - local left_shift = 19 -- horizontal shift left for the longer "SECONDARY" name - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 - local user_entry = {} -- compile a bunch of edit fields - local message = "m. " .. start -- measure selection information - if stop ~= start then -- multiple measures - message = "m" .. message .. "-" .. stop - end - - local dialog = finale.FCCustomLuaWindow() - local str = finale.FCString() - str.LuaString = plugindef() -- get script name - dialog:SetTitle(str) - dialog:CreateHorizontalLine(horiz[1], vert[6] - 9, horiz[5] + 80) - - local texts = { -- words, horiz, vert position - { "TIME SIGNATURE:", horiz[1], vert[1] }, - { "TOP", horiz[3], vert[1] }, { "BOTTOM", horiz[4], vert[1] }, - { "PRIMARY", horiz[2], vert[2] }, { "Selection:", horiz[6], vert[2]+ 5 }, - { "SECONDARY", horiz[2] - left_shift, vert[3] }, { message, horiz[6], vert[3] }, -- "mm. START-STOP" - { "TERTIARY", horiz[2] - 3, vert[4] }, - { "('TOP' entries can include integers joined by '+')", horiz[2] - 30, vert[5] }, - { "DISPLAY SIGNATURE:", horiz[1], vert[6] }, { "(set to \"0\" for none)", horiz[3], vert[6] }, - { "PRIMARY", horiz[2], vert[7] }, - { "SECONDARY", horiz[2] - left_shift, vert[8] }, - { "TERTIARY", horiz[2] - 3, vert[9] }, - } - for i,v in ipairs(texts) do -- write static text items - local static = dialog:CreateStatic(v[2], v[3]) - str.LuaString = v[1] - static:SetText(str) - static:SetWidth( (#v[1] * 6) + 20 ) -- wide enough for item text (circa 6 pix per char plus a bit) - end - - for i, v in ipairs(meters) do -- create all meter numeric entry boxes - local vert_step = math.floor((i - 1) / 2) + 2 - if i > 6 then - vert_step = vert_step + 2 -- middle two lines are instructions - end - local top_bottom = 4 - (i % 2) -- 'TOP' or 'BOTTOM' position x-value - user_entry[i] = dialog:CreateEdit(horiz[top_bottom], vert[vert_step] - mac_offset) - user_entry[i]:SetWidth(70) - str.LuaString = v - if i % 2 == 1 then -- string for TOP meter number - user_entry[i]:SetText(str) -- string for TOP (split into tables later) - else -- plain integer for BOTTOM meter number - user_entry[i]:SetInteger(v) -- integer for BOTTOM - end - end - - -- "CLEAR ALL" button to reset meter defaults - local clear_button = dialog:CreateButton(horiz[5], vert[1] - 2) - str.LuaString = "Clear All" - clear_button:SetWidth(80) - clear_button:SetText(str) - - dialog:RegisterHandleControlEvent ( clear_button, - function(control) - local str = finale.FCString() - str.LuaString = "4" - user_entry[1]:SetText(str) - user_entry[2]:SetInteger(4) - str.LuaString = "0" - for i = 3, 11, 2 do -- clear all other edit boxes - user_entry[i]:SetText(str) - user_entry[i + 1]:SetInteger(0) - end - user_entry[1]:SetFocus() - end - ) - dialog:CreateOkButton() - dialog:CreateCancelButton() - local ok = dialog:ExecuteModal(nil) -- run the dialog - - for i = 1, #meters do -- collate user input - if i % 2 == 1 then -- string for TOP - user_entry[i]:GetText(str) - meters[i] = str.LuaString - else -- integer for BOTTOM - meters[i] = user_entry[i]:GetInteger() - end - end - return ok -end - -function encode_existing_meter(time_sig, meters, index_offset) - -- convert existing meter to our { meters } filing method - if time_sig.CompositeTop then - local comp_top = time_sig:CreateCompositeTop() - local group_count = comp_top:GetGroupCount() - if group_count > 3 then - group_count = 3 -- three composites max - end - for i = 0, group_count - 1 do -- add elements of each group - local top_string = "" -- empty string to index_offset - for element_count = 0, comp_top:GetGroupElementCount(i) - 1 do - if element_count > 0 then -- plus sign after first number - top_string = top_string .. "+" - end - top_string = top_string .. comp_top:GetGroupElementBeats(i, element_count) - end - meters[(i * 2) + index_offset] = top_string -- save the string result - end - else -- non-composite TOP, simple number string - meters[index_offset] = "" .. time_sig.Beats - end - - if time_sig.CompositeBottom then - local comp_bottom = time_sig:CreateCompositeBottom() - local group_count = comp_bottom:GetGroupCount() - if group_count > 3 then - group_count = 3 -- 3 composites max - end - for i = 0, group_count - 1 do - -- assume one element for each composite bottom - local beat_duration = comp_bottom:GetGroupElementBeatDuration(i, 0) - local bottom_offset = (i * 2) + index_offset -- offset for individual composites - if beat_duration % 3 == 0 and not string.find(meters[bottom_offset], "+") then - meters[bottom_offset] = "" .. ( meters[bottom_offset] * 3 ) -- create compound meter - beat_duration = beat_duration / 3 - end - meters[bottom_offset + 1] = math.floor(4096 / beat_duration) -- round to integer in case - end - else -- non-composite BOTTOM - local beat_duration = time_sig.BeatDuration - if beat_duration % 3 == 0 and not string.find(meters[index_offset], "+") then - meters[index_offset] = "" .. ( meters[index_offset] * 3 ) -- create compound meter - beat_duration = beat_duration / 3 - end - meters[index_offset + 1] = math.floor(4096 / beat_duration) -- round to integer in case - end -end - -function copy_meters_from_score(meters, measure_number) - local measure = finale.FCMeasure() - measure:Load(measure_number) - encode_existing_meter(measure:GetTimeSignature(), meters, 1) -- encode primary meter - offset to meters[1] - if measure.UseTimeSigForDisplay then -- encode DISPLAY meter - offset to meters[7] - encode_existing_meter(measure.TimeSignatureForDisplay, meters, 7) - end -end - -function is_positive_integer(x) - return x ~= nil and x > 0 and x % 1 == 0 -end - -function is_power_of_two(num) - local current = 1 - while current < num do - current = current * 2 - end - return current == num -end - -function convert_meter_pairs_to_numbers(top_index, meters) - -- 'TOP' indexed meter is a string of numbers, possibly joined by "+" signs - -- convert to a table of simple integers - if meters[top_index] == "0" or meters[top_index] == "" then - meters[top_index] = { 0 } -- zero array for numerators - if top_index == 1 then - return "Primary time signature can not be zero" - else - return "" -- nil result ... BOTTOM (denominator) is irrelevant - end - end - if string.find(meters[top_index], "[+ -/]") then -- TOP number string contains "+" or other divider - -- COMPLEX string numerator: split into integers - local string_copy = meters[top_index] -- copy the string - meters[top_index] = {} -- start over with empty table of integers - for split_number in string.gmatch(string_copy, "[0-9]+") do -- split numbers - local as_number = tonumber(split_number) - if not is_positive_integer(as_number) then -- positive integer resultor? - return "All numbers must be positive integers\n(not " .. as_number .. ")" - end - table.insert(meters[top_index], as_number) -- else add integer to table - end - else -- simple integer, re-store as single-element table - local as_number = tonumber(meters[top_index]) - if not is_positive_integer(as_number) then - return "All numbers must be positive integers\n(not " .. as_number .. ")" - end - meters[top_index] = { as_number } -- save single integer table - end - - -- 'BOTTOM' numeric DENOMINATOR (top_index + 1) - -- must be simple integer, power of 2 ... convert NOTE NUMBER to EDU value, with compounding - local bottom_index = top_index + 1 - local denominator = meters[bottom_index] - if denominator == 0 then -- no meter here! - return "" - end - if not is_positive_integer(denominator) then - return "All numbers must be positive integers\n(not " .. denominator .. ")" - end - if denominator <= 64 then -- must be a NOTE VALUE (64th note or smaller) not EDU - if not is_power_of_two(denominator) then - return "Denominators must be powers of 2\n(not " .. denominator .. ")" - end - denominator = 4096 / denominator -- convert to EDU - -- check for COMPOUND meter - if #meters[top_index] == 1 then -- single number, simple numerator - local numerator = meters[top_index][1] - -- convert to COMPOUND METER if not a display meter (top_index >= 7) - if top_index < 7 and numerator % 3 == 0 and denominator < 1024 then -- (8th notes or smaller) - meters[top_index][1] = numerator / 3 -- numerator divide by 3 - denominator = denominator * 3 -- denominator multiply by 3 - end - end - meters[bottom_index] = denominator - end - return "" -- no errors -end - -function new_composite_top(numerator_valuesA, numerator_valuesB, numerator_valuesC) - -- each numerator_values is a table of one or more integers to be composite meter numerators - local composite_top = finale.FCCompositeTimeSigTop() - local group = composite_top:AddGroup(#numerator_valuesA) - for i = 1, #numerator_valuesA do - composite_top:SetGroupElementBeats(group, i - 1, numerator_valuesA[i]) - end - - if numerator_valuesB[1] ~= 0 then -- secondary numerator is required - group = composite_top:AddGroup(#numerator_valuesB) - for i = 1, #numerator_valuesB do - composite_top:SetGroupElementBeats(group, i - 1, numerator_valuesB[i]) - end - -- only add numerator_valuesC if numerator_valuesB is non-nil - if numerator_valuesC[1] ~= 0 then -- non-nul tertiary - group = composite_top:AddGroup(#numerator_valuesC) - for i = 1, #numerator_valuesC do - composite_top:SetGroupElementBeats(group, i - 1, numerator_valuesC[i]) - end - end - end - composite_top:SaveAll() - return composite_top -end - -function new_composite_bottom(denominatorA, denominatorB, denominatorC) - -- each number is an EDU value for meter denominator (bottom number) - local composite_bottom = finale.FCCompositeTimeSigBottom() - local new_group = composite_bottom:AddGroup(1) - composite_bottom:SetGroupElementBeatDuration(new_group, 0, denominatorA) - - if denominatorB > 0 then - new_group = composite_bottom:AddGroup(1) - composite_bottom:SetGroupElementBeatDuration(new_group, 0, denominatorB) - -- only consider denominatorC if denominatorB is non-zero - if denominatorC > 0 then - new_group = composite_bottom:AddGroup(1) - composite_bottom:SetGroupElementBeatDuration(new_group, 0, denominatorC) - end - end - composite_bottom:SaveAll() - return composite_bottom -end - -function fix_new_top(composite_top, time_sig, numerator) - if composite_top ~= nil then -- COMPOSITE top - time_sig:SaveNewCompositeTop(composite_top) - else - if time_sig:GetCompositeTop() then - time_sig:RemoveCompositeTop(numerator) - else - time_sig.Beats = numerator -- simple time sig - end - end -end - -function fix_new_bottom(composite_bottom, time_sig, denominator) - if composite_bottom ~= nil then -- COMPOSITE bottom - time_sig:SaveNewCompositeBottom(composite_bottom) - else - if time_sig:GetCompositeBottom() then - time_sig:RemoveCompositeBottom(denominator) - else - time_sig.BeatDuration = denominator - end - end -end - -function create_new_meter() - local region = finenv.Region() - -- preset "all zero" basic meters - 6 sets of top/bottom pairs - -- "real" meter 1 + 2 + 3 | display meter 1 + 2 + 3 - local meters = { "4", 4, "0", 0, "0", 0, "0", 0, "0", 0, "0", 0, } - local result = "" -- holder for error messages - - copy_meters_from_score(meters, region.StartMeasure) -- examine and encode original meter from start of selection - local ok = user_chooses_meter(meters, region) -- user choices stored in meters { } - if ok ~= finale.EXECMODAL_OK then - return -- user cancelled - end - - for index_pairs = 1, 11, 2 do -- check 6 top/bottom pairs - result = convert_meter_pairs_to_numbers(index_pairs, meters) - if result ~= "" then - finenv.UI():AlertNeutral(plugindef(), result) - return - end - end - -- ----------- - -- NOTE that "meters" array now equals - -- TOPS (1,3,5 / 7,9,11) = arrays of integers, BOTTOMS (2,4,6 / 8,10,12) = simple integers - -- ----------- - -- new COMPOSITE METERS TABLE to hold FOUR combined composite Time Signatures: - -- "REAL" = 1 (top) / 2 (bottom) | "DISPLAY" = 3 (top) / 4 (bottom) - local composites = { nil, nil, nil, nil } - for i = 0, 1 do -- PRIMARY meters then DISPLAY meters - local step = i * 6 -- 6 steps between "real" and "display" meter numbers in { meters } - if #meters[step + 1] > 1 or meters[step + 3][1] ~= 0 then -- composite TOP - composites[(i * 2) + 1] = new_composite_top(meters[step + 1], meters[step + 3], meters[step + 5]) - end - -- composite BOTTOM? - if composites[(i * 2) + 1] ~= nil and meters[step + 3][1] ~= 0 then - composites[(i * 2) + 2] = new_composite_bottom(meters[step + 2], meters[step + 4], meters[step + 6]) - end - end - - -- ----------- - region.StartMeasurePos = 0 -- set to whole stack (in case it is safer) - region:SetEndMeasurePosRight() - region:SetFullMeasureStack() - local measures = finale.FCMeasures() - measures:LoadRegion(region) - - for measure in each(measures) do - local time_sig = measure:GetTimeSignature() - fix_new_top(composites[1], time_sig, meters[1][1]) - fix_new_bottom(composites[2], time_sig, meters[2]) - time_sig:Save() - - if meters[7][1] ~= 0 then -- new, unique Display meter - measure.UseTimeSigForDisplay = true - local display_sig = measure.TimeSignatureForDisplay - if display_sig then - fix_new_top(composites[3], display_sig, meters[7][1]) - fix_new_bottom(composites[4], display_sig, meters[8]) - display_sig:Save() - end - else -- suppress display time_sig - measure.UseTimeSigForDisplay = false - end - measure:Save() - end -end - -create_new_meter() +]]return"Meter Set Numeric","Meter Set Numeric","Set the Meter Numerically"end;function user_chooses_meter(a,b)local c={0,70,130,210,290,300}local d={0,20,40,60,82,115,135,155,175}local e=b.StartMeasure;local f=b.EndMeasure;local g=19;local h=finenv.UI():IsOnMac()and 3 or 0;local i={}local j="m. "..e;if f~=e then j="m"..j.."-"..f end;local k=finale.FCCustomLuaWindow()local l=finale.FCString()l.LuaString=plugindef()k:SetTitle(l)k:CreateHorizontalLine(c[1],d[6]-9,c[5]+80)local m={{"TIME SIGNATURE:",c[1],d[1]},{"TOP",c[3],d[1]},{"BOTTOM",c[4],d[1]},{"PRIMARY",c[2],d[2]},{"Selection:",c[6],d[2]+5},{"SECONDARY",c[2]-g,d[3]},{j,c[6],d[3]},{"TERTIARY",c[2]-3,d[4]},{"('TOP' entries can include integers joined by '+')",c[2]-30,d[5]},{"DISPLAY SIGNATURE:",c[1],d[6]},{"(set to \"0\" for none)",c[3],d[6]},{"PRIMARY",c[2],d[7]},{"SECONDARY",c[2]-g,d[8]},{"TERTIARY",c[2]-3,d[9]}}for n,o in ipairs(m)do local p=k:CreateStatic(o[2],o[3])l.LuaString=o[1]p:SetText(l)p:SetWidth(#o[1]*6+20)end;for n,o in ipairs(a)do local q=math.floor((n-1)/2)+2;if n>6 then q=q+2 end;local r=4-n%2;i[n]=k:CreateEdit(c[r],d[q]-h)i[n]:SetWidth(70)l.LuaString=o;if n%2==1 then i[n]:SetText(l)else i[n]:SetInteger(o)end end;local s=k:CreateButton(c[5],d[1]-2)l.LuaString="Clear All"s:SetWidth(80)s:SetText(l)k:RegisterHandleControlEvent(s,function(t)local l=finale.FCString()l.LuaString="4"i[1]:SetText(l)i[2]:SetInteger(4)l.LuaString="0"for n=3,11,2 do i[n]:SetText(l)i[n+1]:SetInteger(0)end;i[1]:SetFocus()end)k:CreateOkButton()k:CreateCancelButton()local u=k:ExecuteModal(nil)for n=1,#a do if n%2==1 then i[n]:GetText(l)a[n]=l.LuaString else a[n]=i[n]:GetInteger()end end;return u end;function encode_existing_meter(v,a,w)if v.CompositeTop then local x=v:CreateCompositeTop()local y=x:GetGroupCount()if y>3 then y=3 end;for n=0,y-1 do local z=""for A=0,x:GetGroupElementCount(n)-1 do if A>0 then z=z.."+"end;z=z..x:GetGroupElementBeats(n,A)end;a[n*2+w]=z end else a[w]=""..v.Beats end;if v.CompositeBottom then local B=v:CreateCompositeBottom()local y=B:GetGroupCount()if y>3 then y=3 end;for n=0,y-1 do local C=B:GetGroupElementBeatDuration(n,0)local D=n*2+w;if C%3==0 and not string.find(a[D],"+")then a[D]=""..a[D]*3;C=C/3 end;a[D+1]=math.floor(4096/C)end else local C=v.BeatDuration;if C%3==0 and not string.find(a[w],"+")then a[w]=""..a[w]*3;C=C/3 end;a[w+1]=math.floor(4096/C)end end;function copy_meters_from_score(a,E)local F=finale.FCMeasure()F:Load(E)encode_existing_meter(F:GetTimeSignature(),a,1)if F.UseTimeSigForDisplay then encode_existing_meter(F.TimeSignatureForDisplay,a,7)end end;function is_positive_integer(G)return G~=nil and G>0 and G%1==0 end;function is_power_of_two(H)local I=1;while I0 then Z=Y:AddGroup(1)Y:SetGroupElementBeatDuration(Z,0,W)if X>0 then Z=Y:AddGroup(1)Y:SetGroupElementBeatDuration(Z,0,X)end end;Y:SaveAll()return Y end;function fix_new_top(T,v,P)if T~=nil then v:SaveNewCompositeTop(T)else if v:GetCompositeTop()then v:RemoveCompositeTop(P)else v.Beats=P end end end;function fix_new_bottom(Y,v,O)if Y~=nil then v:SaveNewCompositeBottom(Y)else if v:GetCompositeBottom()then v:RemoveCompositeBottom(O)else v.BeatDuration=O end end end;function create_new_meter()local b=finenv.Region()local a={"4",4,"0",0,"0",0,"0",0,"0",0,"0",0}local _=""copy_meters_from_score(a,b.StartMeasure)local u=user_chooses_meter(a,b)if u~=finale.EXECMODAL_OK then return end;for a0=1,11,2 do _=convert_meter_pairs_to_numbers(a0,a)if _~=""then finenv.UI():AlertNeutral(plugindef(),_)return end end;local a1={nil,nil,nil,nil}for n=0,1 do local a2=n*6;if#a[a2+1]>1 or a[a2+3][1]~=0 then a1[n*2+1]=new_composite_top(a[a2+1],a[a2+3],a[a2+5])end;if a1[n*2+1]~=nil and a[a2+3][1]~=0 then a1[n*2+2]=new_composite_bottom(a[a2+2],a[a2+4],a[a2+6])end end;b.StartMeasurePos=0;b:SetEndMeasurePosRight()b:SetFullMeasureStack()local a3=finale.FCMeasures()a3:LoadRegion(b)for F in each(a3)do local v=F:GetTimeSignature()fix_new_top(a1[1],v,a[1][1])fix_new_bottom(a1[2],v,a[2])v:Save()if a[7][1]~=0 then F.UseTimeSigForDisplay=true;local a4=F.TimeSignatureForDisplay;if a4 then fix_new_top(a1[3],a4,a[7][1])fix_new_bottom(a1[4],a4,a[8])a4:Save()end else F.UseTimeSigForDisplay=false end;F:Save()end end;create_new_meter() \ No newline at end of file diff --git a/dist/midi_duration.lua b/dist/midi_duration.lua index 6e09bed7..7f668622 100644 --- a/dist/midi_duration.lua +++ b/dist/midi_duration.lua @@ -1,97 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Carl Vine" - finaleplugin.AuthorURL = "http://carlvine.com" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "v1.3" - finaleplugin.Date = "2022/06/13" - finaleplugin.CategoryTags = "MIDI" - finaleplugin.Notes = [[ +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Carl Vine"finaleplugin.AuthorURL="http://carlvine.com"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="v1.3"finaleplugin.Date="2022/06/13"finaleplugin.CategoryTags="MIDI"finaleplugin.Notes=[[ Change the playback START and STOP times for every note in the selected area on one or all layers. To affect playback "Note Durations" must be enabled under "Playback/Record Options". -]] - return "MIDI Duration", "MIDI Duration", "Change MIDI note start and stop times" -end - --- RetainLuaState will return global variables: --- start_offset, stop_offset and layer_number - -function show_error(error_type, actual_value) - local errors = { - bad_offset = "Offset times must be reasonable,\nsay -9999 to 9999\n(not ", - bad_layer_number = "Layer number must be an\ninteger between zero and 4\n(not ", - } - finenv.UI():AlertNeutral("script: " .. plugindef(), errors[error_type] .. actual_value .. ")") -end - -function get_user_choices() - local current_vert, vert_step = 10, 25 - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 -- extra y-offset for Mac text box - local edit_horiz = 120 - - local dialog = finale.FCCustomWindow() - local str = finale.FCString() - str.LuaString = plugindef() - dialog:SetTitle(str) - - local answer = {} - local texts = { -- static text, default value - { "Start time:", start_offset or 0 }, - { "Stop time:", stop_offset or 0 }, - { "Layer# 1-4 (0 = all):", layer_number or 0 }, - } - - for i,v in ipairs(texts) do - str.LuaString = v[1] - local static = dialog:CreateStatic(0, current_vert) - static:SetText(str) - static:SetWidth(edit_horiz) - answer[i] = dialog:CreateEdit(edit_horiz, current_vert - mac_offset) - answer[i]:SetInteger(v[2]) - current_vert = current_vert + vert_step - end - - dialog:CreateOkButton() - dialog:CreateCancelButton() - return (dialog:ExecuteModal(nil) == finale.EXECMODAL_OK), - answer[1]:GetInteger(), answer[2]:GetInteger(), answer[3]:GetInteger() -end - -function change_midi_duration() - local ok = false - is_ok, start_offset, stop_offset, layer_number = get_user_choices() - if not is_ok then - return - end -- user cancelled - - if start_offset < -9999 or start_offset > 9999 or stop_offset < -9999 or stop_offset > 9999 then - show_error("bad_offset", start_offset .. " / " .. stop_offset) - return - end - if layer_number < 0 or layer_number > 4 then - show_error("bad_layer_number", layer_number) - return - end - if finenv.RetainLuaState ~= nil then - finenv.RetainLuaState = true - end - - for entry in eachentrysaved(finenv.Region(), layer_number) do - local perf_mod = finale.FCPerformanceMod() - if entry:IsNote() then - perf_mod:SetNoteEntry(entry) - for note in each(entry) do - perf_mod:LoadAt(note) -- don't change durations of tied notes! - if not note.TieBackwards then - perf_mod.StartOffset = start_offset - end - if not note.Tie then - perf_mod.EndOffset = stop_offset - end - perf_mod:SaveAt(note) - end - end - end -end - -change_midi_duration() +]]return"MIDI Duration","MIDI Duration","Change MIDI note start and stop times"end;function show_error(a,b)local c={bad_offset="Offset times must be reasonable,\nsay -9999 to 9999\n(not ",bad_layer_number="Layer number must be an\ninteger between zero and 4\n(not "}finenv.UI():AlertNeutral("script: "..plugindef(),c[a]..b..")")end;function get_user_choices()local d,e=10,25;local f=finenv.UI():IsOnMac()and 3 or 0;local g=120;local h=finale.FCCustomWindow()local i=finale.FCString()i.LuaString=plugindef()h:SetTitle(i)local j={}local k={{"Start time:",start_offset or 0},{"Stop time:",stop_offset or 0},{"Layer# 1-4 (0 = all):",layer_number or 0}}for l,m in ipairs(k)do i.LuaString=m[1]local n=h:CreateStatic(0,d)n:SetText(i)n:SetWidth(g)j[l]=h:CreateEdit(g,d-f)j[l]:SetInteger(m[2])d=d+e end;h:CreateOkButton()h:CreateCancelButton()return h:ExecuteModal(nil)==finale.EXECMODAL_OK,j[1]:GetInteger(),j[2]:GetInteger(),j[3]:GetInteger()end;function change_midi_duration()local o=false;is_ok,start_offset,stop_offset,layer_number=get_user_choices()if not is_ok then return end;if start_offset<-9999 or start_offset>9999 or stop_offset<-9999 or stop_offset>9999 then show_error("bad_offset",start_offset.." / "..stop_offset)return end;if layer_number<0 or layer_number>4 then show_error("bad_layer_number",layer_number)return end;if finenv.RetainLuaState~=nil then finenv.RetainLuaState=true end;for p in eachentrysaved(finenv.Region(),layer_number)do local q=finale.FCPerformanceMod()if p:IsNote()then q:SetNoteEntry(p)for r in each(p)do q:LoadAt(r)if not r.TieBackwards then q.StartOffset=start_offset end;if not r.Tie then q.EndOffset=stop_offset end;q:SaveAt(r)end end end end;change_midi_duration() \ No newline at end of file diff --git a/dist/midi_velocity.lua b/dist/midi_velocity.lua index b0301660..e159cb5e 100644 --- a/dist/midi_velocity.lua +++ b/dist/midi_velocity.lua @@ -1,12 +1,4 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Carl Vine" - finaleplugin.AuthorURL = "http://carlvine.com/?cv=lua" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "v1.2" - finaleplugin.Date = "2022/06/14" - finaleplugin.CategoryTags = "MIDI, Playback" - finaleplugin.Notes = [[ +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Carl Vine"finaleplugin.AuthorURL="http://carlvine.com/?cv=lua"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="v1.2"finaleplugin.Date="2022/06/14"finaleplugin.CategoryTags="MIDI, Playback"finaleplugin.Notes=[[ Change the playback Key Velocity for every note in the selected area in one or all layers. "Key Velocities" must be enabled under "Playback/Record Options" to affect playback. Note that key velocity will not affect every type of playback especially if Human Playback is active. @@ -14,85 +6,4 @@ function plugindef() Side-note: selecting the MIDI tool, choosing "Velocity" then "Set to" is moderately convenient but doesn't offer setting key velocity on a single chosen layer. This script also remembers your choices between invocations. -]] - return "MIDI Velocity", "MIDI Velocity", "Change MIDI Velocity" -end - --- RetainLuaState will return global variables: --- key_velocity and layer_number - -function show_error(error_type, actual_value) - local errors = { - bad_velocity = "Velocity must be an\ninteger between 0 and 127\n(not ", - bad_layer_number = "Layer number must be an\ninteger between zero and 4\n(not ", - } - finenv.UI():AlertNeutral("script: " .. plugindef(), errors[error_type] .. actual_value .. ")") -end - -function get_user_choices(basekey) - local current_vert, vert_step = 10, 25 - local mac_offset = finenv.UI():IsOnMac() and 3 or 0 -- extra y-offset for Mac text box - local edit_horiz = 120 - - local dialog = finale.FCCustomWindow() - local str = finale.FCString() - str.LuaString = plugindef() - dialog:SetTitle(str) - - local answer = {} - local texts = { -- static text, default value - { "Key Velocity (0-127):", key_velocity or basekey }, - { "Layer 1-4 (0 = all):", layer_number or 0 }, - } - for i,v in ipairs(texts) do - str.LuaString = v[1] - local static = dialog:CreateStatic(0, current_vert) - static:SetText(str) - static:SetWidth(edit_horiz) - answer[i] = dialog:CreateEdit(edit_horiz, current_vert - mac_offset) - answer[i]:SetInteger(v[2]) - current_vert = current_vert + vert_step - end - - dialog:CreateOkButton() - dialog:CreateCancelButton() - return (dialog:ExecuteModal(nil) == finale.EXECMODAL_OK), answer[1]:GetInteger(), answer[2]:GetInteger() -end - -function change_velocity() - local prefs = finale.FCPlaybackPrefs() - prefs:Load(1) - local basekey = prefs:GetBaseKeyVelocity() - - local is_ok = false -- key_velocity and layer_number are globals - is_ok, key_velocity, layer_number = get_user_choices(basekey) - if not is_ok then -- user cancelled - return - end - if key_velocity < 0 or key_velocity > 127 then - show_error("bad_velocity", key_velocity) - return - end - if layer_number < 0 or layer_number > 4 then - show_error("bad_layer_number", layer_number) - return - end - if finenv.RetainLuaState ~= nil then - finenv.RetainLuaState = true - end - - -- don't forget to offset velocity to playback "base" velocity - for entry in eachentrysaved(finenv.Region(), layer_number) do - local pm = finale.FCPerformanceMod() - if entry:IsNote() then - pm:SetNoteEntry(entry) - for note in each(entry) do - pm:LoadAt(note) - pm.VelocityDelta = key_velocity - basekey - pm:SaveAt(note) - end - end - end -end - -change_velocity() +]]return"MIDI Velocity","MIDI Velocity","Change MIDI Velocity"end;function show_error(a,b)local c={bad_velocity="Velocity must be an\ninteger between 0 and 127\n(not ",bad_layer_number="Layer number must be an\ninteger between zero and 4\n(not "}finenv.UI():AlertNeutral("script: "..plugindef(),c[a]..b..")")end;function get_user_choices(d)local e,f=10,25;local g=finenv.UI():IsOnMac()and 3 or 0;local h=120;local i=finale.FCCustomWindow()local j=finale.FCString()j.LuaString=plugindef()i:SetTitle(j)local k={}local l={{"Key Velocity (0-127):",key_velocity or d},{"Layer 1-4 (0 = all):",layer_number or 0}}for m,n in ipairs(l)do j.LuaString=n[1]local o=i:CreateStatic(0,e)o:SetText(j)o:SetWidth(h)k[m]=i:CreateEdit(h,e-g)k[m]:SetInteger(n[2])e=e+f end;i:CreateOkButton()i:CreateCancelButton()return i:ExecuteModal(nil)==finale.EXECMODAL_OK,k[1]:GetInteger(),k[2]:GetInteger()end;function change_velocity()local p=finale.FCPlaybackPrefs()p:Load(1)local d=p:GetBaseKeyVelocity()local q=false;q,key_velocity,layer_number=get_user_choices(d)if not q then return end;if key_velocity<0 or key_velocity>127 then show_error("bad_velocity",key_velocity)return end;if layer_number<0 or layer_number>4 then show_error("bad_layer_number",layer_number)return end;if finenv.RetainLuaState~=nil then finenv.RetainLuaState=true end;for r in eachentrysaved(finenv.Region(),layer_number)do local s=finale.FCPerformanceMod()if r:IsNote()then s:SetNoteEntry(r)for t in each(r)do s:LoadAt(t)s.VelocityDelta=key_velocity-d;s:SaveAt(t)end end end end;change_velocity() \ No newline at end of file diff --git a/dist/note_add_augmentation_dots.lua b/dist/note_add_augmentation_dots.lua index e2e21c46..dd51ce8f 100644 --- a/dist/note_add_augmentation_dots.lua +++ b/dist/note_add_augmentation_dots.lua @@ -1,424 +1 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Robert Patterson" - finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/" - finaleplugin.Version = "1.0.1" - finaleplugin.Date = "June 12, 2020" - finaleplugin.CategoryTags = "Note" - return "Add Augmentation Dots", "Add Augmentation Dots", - "Add an augmentation dot to all notes and rests in selected region." -end - ---[[ -$module Note Entry -]] -- -local note_entry = {} - ---[[ -% get_music_region - -Returns an intance of `FCMusicRegion` that corresponds to the metric location of the input note entry. - -@ entry (FCNoteEntry) -: (FCMusicRegion) -]] -function note_entry.get_music_region(entry) - local exp_region = finale.FCMusicRegion() - exp_region:SetCurrentSelection() -- called to match the selected IU list (e.g., if using Staff Sets) - exp_region.StartStaff = entry.Staff - exp_region.EndStaff = entry.Staff - exp_region.StartMeasure = entry.Measure - exp_region.EndMeasure = entry.Measure - exp_region.StartMeasurePos = entry.MeasurePos - exp_region.EndMeasurePos = entry.MeasurePos - return exp_region -end - --- entry_metrics can be omitted, in which case they are constructed and released here --- return entry_metrics, loaded_here -local use_or_get_passed_in_entry_metrics = function(entry, entry_metrics) - if entry_metrics then - return entry_metrics, false - end - entry_metrics = finale.FCEntryMetrics() - if entry_metrics:Load(entry) then - return entry_metrics, true - end - return nil, false -end - ---[[ -% get_evpu_notehead_height - -Returns the calculated height of the notehead rectangle. - -@ entry (FCNoteEntry) - -: (number) the EVPU height -]] -function note_entry.get_evpu_notehead_height(entry) - local highest_note = entry:CalcHighestNote(nil) - local lowest_note = entry:CalcLowestNote(nil) - local evpu_height = (2 + highest_note:CalcStaffPosition() - lowest_note:CalcStaffPosition()) * 12 -- 12 evpu per staff step; add 2 staff steps to accommodate for notehead height at top and bottom - return evpu_height -end - ---[[ -% get_top_note_position - -Returns the vertical page coordinate of the top of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_top_note_position(entry, entry_metrics) - local retval = -math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if not entry:CalcStemUp() then - retval = entry_metrics.TopPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.BottomPosition + scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% get_bottom_note_position - -Returns the vertical page coordinate of the bottom of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_bottom_note_position(entry, entry_metrics) - local retval = math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if entry:CalcStemUp() then - retval = entry_metrics.BottomPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.TopPosition - scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% calc_widths - -Get the widest left-side notehead width and widest right-side notehead width. - -@ entry (FCNoteEntry) -: (number, number) widest left-side notehead width and widest right-side notehead width -]] -function note_entry.calc_widths(entry) - local left_width = 0 - local right_width = 0 - for note in each(entry) do - local note_width = note:CalcNoteheadWidth() - if note_width > 0 then - if note:CalcRightsidePlacement() then - if note_width > right_width then - right_width = note_width - end - else - if note_width > left_width then - left_width = note_width - end - end - end - end - return left_width, right_width -end - --- These functions return the offset for an expression handle. --- Expression handles are vertical when they are left-aligned --- with the primary notehead rectangle. - ---[[ -% calc_left_of_all_noteheads - -Calculates the handle offset for an expression with "Left of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_all_noteheads(entry) - if entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return -left -end - ---[[ -% calc_left_of_primary_notehead - -Calculates the handle offset for an expression with "Left of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_primary_notehead(entry) - return 0 -end - ---[[ -% calc_center_of_all_noteheads - -Calculates the handle offset for an expression with "Center of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - local width_centered = (left + right) / 2 - if not entry:CalcStemUp() then - width_centered = width_centered - left - end - return width_centered -end - ---[[ -% calc_center_of_primary_notehead - -Calculates the handle offset for an expression with "Center of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_primary_notehead(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left / 2 - end - return right / 2 -end - ---[[ -% calc_stem_offset - -Calculates the offset of the stem from the left edge of the notehead rectangle. Eventually the PDK Framework may be able to provide this instead. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset of stem from the left edge of the notehead rectangle. -]] -function note_entry.calc_stem_offset(entry) - if not entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return left -end - ---[[ -% calc_right_of_all_noteheads - -Calculates the handle offset for an expression with "Right of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_right_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left + right - end - return right -end - ---[[ -% calc_note_at_index - -This function assumes `for note in each(note_entry)` always iterates in the same direction. -(Knowing how the Finale PDK works, it probably iterates from bottom to top note.) -Currently the PDK Framework does not seem to offer a better option. - -@ entry (FCNoteEntry) -@ note_index (number) the zero-based index -]] -function note_entry.calc_note_at_index(entry, note_index) - local x = 0 - for note in each(entry) do - if x == note_index then - return note - end - x = x + 1 - end - return nil -end - ---[[ -% stem_sign - -This is useful for many x,y positioning fields in Finale that mirror +/- -based on stem direction. - -@ entry (FCNoteEntry) -: (number) 1 if upstem, -1 otherwise -]] -function note_entry.stem_sign(entry) - if entry:CalcStemUp() then - return 1 - end - return -1 -end - ---[[ -% duplicate_note - -@ note (FCNote) -: (FCNote | nil) reference to added FCNote or `nil` if not success -]] -function note_entry.duplicate_note(note) - local new_note = note.Entry:AddNewNote() - if nil ~= new_note then - new_note.Displacement = note.Displacement - new_note.RaiseLower = note.RaiseLower - new_note.Tie = note.Tie - new_note.TieBackwards = note.TieBackwards - end - return new_note -end - ---[[ -% delete_note - -Removes the specified FCNote from its associated FCNoteEntry. - -@ note (FCNote) -: (boolean) true if success -]] -function note_entry.delete_note(note) - local entry = note.Entry - if nil == entry then - return false - end - - -- attempt to delete all associated entry-detail mods, but ignore any failures - finale.FCAccidentalMod():EraseAt(note) - finale.FCCrossStaffMod():EraseAt(note) - finale.FCDotMod():EraseAt(note) - finale.FCNoteheadMod():EraseAt(note) - finale.FCPercussionNoteMod():EraseAt(note) - finale.FCTablatureNoteMod():EraseAt(note) - if finale.FCTieMod then -- added in RGP Lua 0.62 - finale.FCTieMod(finale.TIEMODTYPE_TIESTART):EraseAt(note) - finale.FCTieMod(finale.TIEMODTYPE_TIEEND):EraseAt(note) - end - - return entry:DeleteNote(note) -end - ---[[ -% calc_spans_number_of_octaves - -Calculates the numer of octaves spanned by a chord (considering only staff positions, not accidentals). - -@ entry (FCNoteEntry) the entry to calculate from -: (number) of octaves spanned -]] -function note_entry.calc_spans_number_of_octaves(entry) - local top_note = entry:CalcHighestNote(nil) - local bottom_note = entry:CalcLowestNote(nil) - local displacement_diff = top_note.Displacement - bottom_note.Displacement - local num_octaves = math.ceil(displacement_diff / 7) - return num_octaves -end - ---[[ -% add_augmentation_dot - -Adds an augentation dot to the entry. This works even if the entry already has one or more augmentation dots. - -@ entry (FCNoteEntry) the entry to which to add the augmentation dot -]] -function note_entry.add_augmentation_dot(entry) - -- entry.Duration = entry.Duration | (entry.Duration >> 1) -- For Lua 5.3 and higher - entry.Duration = bit32.bor(entry.Duration, bit32.rshift(entry.Duration, 1)) -end - ---[[ -% get_next_same_v - -Returns the next entry in the same V1 or V2 as the input entry. -If the input entry is V2, only the current V2 launch is searched. -If the input entry is V1, only the current measure and layer is searched. - -@ entry (FCNoteEntry) the entry to process -: (FCNoteEntry) the next entry or `nil` in none -]] -function note_entry.get_next_same_v(entry) - local next_entry = entry:Next() - if entry.Voice2 then - if (nil ~= next_entry) and next_entry.Voice2 then - return next_entry - end - return nil - end - if entry.Voice2Launch then - while (nil ~= next_entry) and next_entry.Voice2 do - next_entry = next_entry:Next() - end - end - return next_entry -end - ---[[ -% hide_stem - -Hides the stem of the entry by replacing it with Shape 0. - -@ entry (FCNoteEntry) the entry to process -]] -function note_entry.hide_stem(entry) - local stem = finale.FCCustomStemMod() - stem:SetNoteEntry(entry) - stem:UseUpStemData(entry:CalcStemUp()) - if stem:LoadFirst() then - stem.ShapeID = 0 - stem:Save() - else - stem.ShapeID = 0 - stem:SaveNew() - end -end - - - - -function note_add_augmentation_dots() - for entry in eachentrysaved(finenv.Region()) do - note_entry.add_augmentation_dot(entry) - end - finenv.Region():RebeamMusic() -end - -note_add_augmentation_dots() +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Robert Patterson"finaleplugin.Copyright="CC0 https://creativecommons.org/publicdomain/zero/1.0/"finaleplugin.Version="1.0.1"finaleplugin.Date="June 12, 2020"finaleplugin.CategoryTags="Note"return"Add Augmentation Dots","Add Augmentation Dots","Add an augmentation dot to all notes and rests in selected region."end;local o=require("library.note_entry")function note_add_augmentation_dots()for p in eachentrysaved(finenv.Region())do o.add_augmentation_dot(p)end;finenv.Region():RebeamMusic()end;note_add_augmentation_dots()end)c("library.note_entry",function(require,n,c,d)local q={}function q.finale_version(r,s,t)local u=bit32.bor(bit32.lshift(math.floor(r),24),bit32.lshift(math.floor(s),20))if t then u=bit32.bor(u,math.floor(t))end;return u end;function q.group_overlaps_region(v,w)if w:IsFullDocumentSpan()then return true end;local x=false;local y=finale.FCSystemStaves()y:LoadAllForRegion(w)for z in each(y)do if v:ContainsStaff(z:GetStaff())then x=true;break end end;if not x then return false end;if v.StartMeasure>w.EndMeasure or v.EndMeasure0 then G=true;break end;E=E+1 end;if G then local H=finale.FCStaffSystem()H:Load(F:GetFirstSystem())return finale.FCCell(H.FirstMeasure,H.TopStaff)end;local I=finale.FCMusicRegion()I:SetFullDocument()return finale.FCCell(I.EndMeasure,I.EndStaff)end;function q.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local J=finale.FCMusicRegion()J:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),J.StartStaff)end;return q.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function q.get_top_left_selected_or_visible_cell()local C=finenv.Region()if not C:IsEmpty()then return finale.FCCell(C.StartMeasure,C.StartStaff)end;return q.get_top_left_visible_cell()end;function q.is_default_measure_number_visible_on_cell(K,L,M,N)local O=finale.FCCurrentStaffSpec()if not O:LoadForCell(L,0)then return false end;if K:GetShowOnTopStaff()and L.Staff==M.TopStaff then return true end;if K:GetShowOnBottomStaff()and L.Staff==M:CalcBottomStaff()then return true end;if O.ShowMeasureNumbers then return not K:GetExcludeOtherStaves(N)end;return false end;function q.is_default_number_visible_and_left_aligned(K,L,P,N,Q)if K.UseScoreInfoForParts then N=false end;if Q and K:GetShowOnMultiMeasureRests(N)then if finale.MNALIGN_LEFT~=K:GetMultiMeasureAlignment(N)then return false end elseif L.Measure==P.FirstMeasure then if not K:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=K:GetStartAlignment(N)then return false end else if not K:GetShowMultiples(N)then return false end;if finale.MNALIGN_LEFT~=K:GetMultipleAlignment(N)then return false end end;return q.is_default_measure_number_visible_on_cell(K,L,P,N)end;function q.update_layout(R,S)R=R or 1;S=S or false;local T=finale.FCPage()if T:Load(R)then T:UpdateLayout(S)end end;function q.get_current_part()local U=finale.FCParts()U:LoadAll()return U:GetCurrent()end;function q.get_page_format_prefs()local V=q.get_current_part()local W=finale.FCPageFormatPrefs()local X=false;if V:IsScore()then X=W:LoadScore()else X=W:LoadParts()end;return W,X end;function q.get_smufl_metadata_file(Y)if not Y then Y=finale.FCFontInfo()Y:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local Z=function(_,Y)local a0=_.."/SMuFL/Fonts/"..Y.Name.."/"..Y.Name..".json"return io.open(a0,"r")end;local a1=""if finenv.UI():IsOnWindows()then a1=os.getenv("LOCALAPPDATA")else a1=os.getenv("HOME").."/Library/Application Support"end;local a2=Z(a1,Y)if nil~=a2 then return a2 end;local a3="/Library/Application Support"if finenv.UI():IsOnWindows()then a3=os.getenv("COMMONPROGRAMFILES")end;return Z(a3,Y)end;function q.is_font_smufl_font(Y)if not Y then Y=finale.FCFontInfo()Y:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=q.finale_version(27,1)then if nil~=Y.IsSMuFLFont then return Y.IsSMuFLFont end end;local a4=q.get_smufl_metadata_file(Y)if nil~=a4 then io.close(a4)return true end;return false end;function q.simple_input(a5,a6)local a7=finale.FCString()a7.LuaString=""local a8=finale.FCString()local a9=160;function format_ctrl(aa,ab,ac,ad)aa:SetHeight(ab)aa:SetWidth(ac)a8.LuaString=ad;aa:SetText(a8)end;title_width=string.len(a5)*6+54;if title_width>a9 then a9=title_width end;text_width=string.len(a6)*6;if text_width>a9 then a9=text_width end;a8.LuaString=a5;local ae=finale.FCCustomLuaWindow()ae:SetTitle(a8)local af=ae:CreateStatic(0,0)format_ctrl(af,16,a9,a6)local ag=ae:CreateEdit(0,20)format_ctrl(ag,20,a9,"")ae:CreateOkButton()ae:CreateCancelButton()function callback(aa)end;ae:RegisterHandleCommand(callback)if ae:ExecuteModal(nil)==finale.EXECMODAL_OK then a7.LuaString=ag:GetText(a7)return a7.LuaString end end;function q.is_finale_object(ah)return ah and type(ah)=="userdata"and ah.ClassName and ah.GetClassID and true or false end;function q.system_indent_set_to_prefs(P,W)W=W or q.get_page_format_prefs()local ai=finale.FCMeasure()local aj=P.FirstMeasure==1;if not aj and ai:Load(P.FirstMeasure)then if ai.ShowFullNames then aj=true end end;if aj and W.UseFirstSystemMargins then P.LeftMargin=W.FirstSystemLeft else P.LeftMargin=W.SystemLeft end;return P:Save()end;return q end)return a("__root") \ No newline at end of file diff --git a/dist/note_bariolage.lua b/dist/note_bariolage.lua index eed9ce51..9be93c89 100644 --- a/dist/note_bariolage.lua +++ b/dist/note_bariolage.lua @@ -1,9 +1,4 @@ -function plugindef() - finaleplugin.Author = "Jacob Winkler" - finaleplugin.Copyright = "2022" - finaleplugin.Version = "1.0" - finaleplugin.Date = "3/18/2022" - finaleplugin.Notes = [[ +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,n,c,d)function plugindef()finaleplugin.Author="Jacob Winkler"finaleplugin.Copyright="2022"finaleplugin.Version="1.0"finaleplugin.Date="3/18/2022"finaleplugin.Notes=[[ USING THE 'BARIOLAGE' SCRIPT This script creates bariolage-style notation where layers 1 and 2 interlock. It works well for material that has even-numbered beam groups like 4x 16th notes or 6x 16th notes (in compound meters). 32nd notes also work. Odd numbers of notes produce undesirable results. @@ -15,533 +10,4 @@ function plugindef() - Any note in layer 1 that is the last note of a beamed group is hidden. - Iterates through the notes in layer 2 and changes the stems of the odd-numbered notes. - Any note in layer 2 that is the beginning of a beamed group is hidden. - ]] - return "Bariolage", "Bariolage", - "Bariolage: Creates alternating layer pattern from layer 1. Doesn't play nicely with odd numbered groups!" -end - ---[[ -$module Layer -]] -- -local layer = {} - ---[[ -% copy - -Duplicates the notes from the source layer to the destination. The source layer remains untouched. - -@ region (FCMusicRegion) the region to be copied -@ source_layer (number) the number (1-4) of the layer to duplicate -@ destination_layer (number) the number (1-4) of the layer to be copied to -]] -function layer.copy(region, source_layer, destination_layer) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - source_layer = source_layer - 1 - destination_layer = destination_layer - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentry_source_layer = finale.FCNoteEntryLayer(source_layer, staffNum, start, stop) - noteentry_source_layer:Load() - local noteentry_destination_layer = noteentry_source_layer:CreateCloneEntries( - destination_layer, staffNum, start) - noteentry_destination_layer:Save() - noteentry_destination_layer:CloneTuplets(noteentry_source_layer) - noteentry_destination_layer:Save() - end -end -- function layer_copy - ---[[ -% clear - -Clears all entries from a given layer. - -@ region (FCMusicRegion) the region to be cleared -@ layer_to_clear (number) the number (1-4) of the layer to clear -]] -function layer.clear(region, layer_to_clear) - layer_to_clear = layer_to_clear - 1 -- Turn 1 based layer to 0 based layer - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer = finale.FCNoteEntryLayer(layer_to_clear, staffNum, start, stop) - noteentrylayer:Load() - noteentrylayer:ClearAllEntries() - end -end - ---[[ -% swap - -Swaps the entries from two different layers (e.g. 1-->2 and 2-->1). - -@ region (FCMusicRegion) the region to be swapped -@ swap_a (number) the number (1-4) of the first layer to be swapped -@ swap_b (number) the number (1-4) of the second layer to be swapped -]] -function layer.swap(region, swap_a, swap_b) - local start = region.StartMeasure - local stop = region.EndMeasure - local sysstaves = finale.FCSystemStaves() - sysstaves:LoadAllForRegion(region) - -- Set layers for 0 based - swap_a = swap_a - 1 - swap_b = swap_b - 1 - for sysstaff in each(sysstaves) do - staffNum = sysstaff.Staff - local noteentrylayer_1 = finale.FCNoteEntryLayer(swap_a, staffNum, start, stop) - noteentrylayer_1:Load() - noteentrylayer_1.LayerIndex = swap_b - -- - local noteentrylayer_2 = finale.FCNoteEntryLayer(swap_b, staffNum, start, stop) - noteentrylayer_2:Load() - noteentrylayer_2.LayerIndex = swap_a - noteentrylayer_1:Save() - noteentrylayer_2:Save() - end -end - - - ---[[ -$module Note Entry -]] -- -local note_entry = {} - ---[[ -% get_music_region - -Returns an intance of `FCMusicRegion` that corresponds to the metric location of the input note entry. - -@ entry (FCNoteEntry) -: (FCMusicRegion) -]] -function note_entry.get_music_region(entry) - local exp_region = finale.FCMusicRegion() - exp_region:SetCurrentSelection() -- called to match the selected IU list (e.g., if using Staff Sets) - exp_region.StartStaff = entry.Staff - exp_region.EndStaff = entry.Staff - exp_region.StartMeasure = entry.Measure - exp_region.EndMeasure = entry.Measure - exp_region.StartMeasurePos = entry.MeasurePos - exp_region.EndMeasurePos = entry.MeasurePos - return exp_region -end - --- entry_metrics can be omitted, in which case they are constructed and released here --- return entry_metrics, loaded_here -local use_or_get_passed_in_entry_metrics = function(entry, entry_metrics) - if entry_metrics then - return entry_metrics, false - end - entry_metrics = finale.FCEntryMetrics() - if entry_metrics:Load(entry) then - return entry_metrics, true - end - return nil, false -end - ---[[ -% get_evpu_notehead_height - -Returns the calculated height of the notehead rectangle. - -@ entry (FCNoteEntry) - -: (number) the EVPU height -]] -function note_entry.get_evpu_notehead_height(entry) - local highest_note = entry:CalcHighestNote(nil) - local lowest_note = entry:CalcLowestNote(nil) - local evpu_height = (2 + highest_note:CalcStaffPosition() - lowest_note:CalcStaffPosition()) * 12 -- 12 evpu per staff step; add 2 staff steps to accommodate for notehead height at top and bottom - return evpu_height -end - ---[[ -% get_top_note_position - -Returns the vertical page coordinate of the top of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_top_note_position(entry, entry_metrics) - local retval = -math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if not entry:CalcStemUp() then - retval = entry_metrics.TopPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.BottomPosition + scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% get_bottom_note_position - -Returns the vertical page coordinate of the bottom of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_bottom_note_position(entry, entry_metrics) - local retval = math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if entry:CalcStemUp() then - retval = entry_metrics.BottomPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.TopPosition - scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% calc_widths - -Get the widest left-side notehead width and widest right-side notehead width. - -@ entry (FCNoteEntry) -: (number, number) widest left-side notehead width and widest right-side notehead width -]] -function note_entry.calc_widths(entry) - local left_width = 0 - local right_width = 0 - for note in each(entry) do - local note_width = note:CalcNoteheadWidth() - if note_width > 0 then - if note:CalcRightsidePlacement() then - if note_width > right_width then - right_width = note_width - end - else - if note_width > left_width then - left_width = note_width - end - end - end - end - return left_width, right_width -end - --- These functions return the offset for an expression handle. --- Expression handles are vertical when they are left-aligned --- with the primary notehead rectangle. - ---[[ -% calc_left_of_all_noteheads - -Calculates the handle offset for an expression with "Left of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_all_noteheads(entry) - if entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return -left -end - ---[[ -% calc_left_of_primary_notehead - -Calculates the handle offset for an expression with "Left of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_primary_notehead(entry) - return 0 -end - ---[[ -% calc_center_of_all_noteheads - -Calculates the handle offset for an expression with "Center of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - local width_centered = (left + right) / 2 - if not entry:CalcStemUp() then - width_centered = width_centered - left - end - return width_centered -end - ---[[ -% calc_center_of_primary_notehead - -Calculates the handle offset for an expression with "Center of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_primary_notehead(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left / 2 - end - return right / 2 -end - ---[[ -% calc_stem_offset - -Calculates the offset of the stem from the left edge of the notehead rectangle. Eventually the PDK Framework may be able to provide this instead. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset of stem from the left edge of the notehead rectangle. -]] -function note_entry.calc_stem_offset(entry) - if not entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return left -end - ---[[ -% calc_right_of_all_noteheads - -Calculates the handle offset for an expression with "Right of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_right_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left + right - end - return right -end - ---[[ -% calc_note_at_index - -This function assumes `for note in each(note_entry)` always iterates in the same direction. -(Knowing how the Finale PDK works, it probably iterates from bottom to top note.) -Currently the PDK Framework does not seem to offer a better option. - -@ entry (FCNoteEntry) -@ note_index (number) the zero-based index -]] -function note_entry.calc_note_at_index(entry, note_index) - local x = 0 - for note in each(entry) do - if x == note_index then - return note - end - x = x + 1 - end - return nil -end - ---[[ -% stem_sign - -This is useful for many x,y positioning fields in Finale that mirror +/- -based on stem direction. - -@ entry (FCNoteEntry) -: (number) 1 if upstem, -1 otherwise -]] -function note_entry.stem_sign(entry) - if entry:CalcStemUp() then - return 1 - end - return -1 -end - ---[[ -% duplicate_note - -@ note (FCNote) -: (FCNote | nil) reference to added FCNote or `nil` if not success -]] -function note_entry.duplicate_note(note) - local new_note = note.Entry:AddNewNote() - if nil ~= new_note then - new_note.Displacement = note.Displacement - new_note.RaiseLower = note.RaiseLower - new_note.Tie = note.Tie - new_note.TieBackwards = note.TieBackwards - end - return new_note -end - ---[[ -% delete_note - -Removes the specified FCNote from its associated FCNoteEntry. - -@ note (FCNote) -: (boolean) true if success -]] -function note_entry.delete_note(note) - local entry = note.Entry - if nil == entry then - return false - end - - -- attempt to delete all associated entry-detail mods, but ignore any failures - finale.FCAccidentalMod():EraseAt(note) - finale.FCCrossStaffMod():EraseAt(note) - finale.FCDotMod():EraseAt(note) - finale.FCNoteheadMod():EraseAt(note) - finale.FCPercussionNoteMod():EraseAt(note) - finale.FCTablatureNoteMod():EraseAt(note) - if finale.FCTieMod then -- added in RGP Lua 0.62 - finale.FCTieMod(finale.TIEMODTYPE_TIESTART):EraseAt(note) - finale.FCTieMod(finale.TIEMODTYPE_TIEEND):EraseAt(note) - end - - return entry:DeleteNote(note) -end - ---[[ -% calc_spans_number_of_octaves - -Calculates the numer of octaves spanned by a chord (considering only staff positions, not accidentals). - -@ entry (FCNoteEntry) the entry to calculate from -: (number) of octaves spanned -]] -function note_entry.calc_spans_number_of_octaves(entry) - local top_note = entry:CalcHighestNote(nil) - local bottom_note = entry:CalcLowestNote(nil) - local displacement_diff = top_note.Displacement - bottom_note.Displacement - local num_octaves = math.ceil(displacement_diff / 7) - return num_octaves -end - ---[[ -% add_augmentation_dot - -Adds an augentation dot to the entry. This works even if the entry already has one or more augmentation dots. - -@ entry (FCNoteEntry) the entry to which to add the augmentation dot -]] -function note_entry.add_augmentation_dot(entry) - -- entry.Duration = entry.Duration | (entry.Duration >> 1) -- For Lua 5.3 and higher - entry.Duration = bit32.bor(entry.Duration, bit32.rshift(entry.Duration, 1)) -end - ---[[ -% get_next_same_v - -Returns the next entry in the same V1 or V2 as the input entry. -If the input entry is V2, only the current V2 launch is searched. -If the input entry is V1, only the current measure and layer is searched. - -@ entry (FCNoteEntry) the entry to process -: (FCNoteEntry) the next entry or `nil` in none -]] -function note_entry.get_next_same_v(entry) - local next_entry = entry:Next() - if entry.Voice2 then - if (nil ~= next_entry) and next_entry.Voice2 then - return next_entry - end - return nil - end - if entry.Voice2Launch then - while (nil ~= next_entry) and next_entry.Voice2 do - next_entry = next_entry:Next() - end - end - return next_entry -end - ---[[ -% hide_stem - -Hides the stem of the entry by replacing it with Shape 0. - -@ entry (FCNoteEntry) the entry to process -]] -function note_entry.hide_stem(entry) - local stem = finale.FCCustomStemMod() - stem:SetNoteEntry(entry) - stem:UseUpStemData(entry:CalcStemUp()) - if stem:LoadFirst() then - stem.ShapeID = 0 - stem:Save() - else - stem.ShapeID = 0 - stem:SaveNew() - end -end - - - ---- -function bariolage() - local region = finenv.Region() - layer.copy(region, 1, 2) - local layer1_ct = 1 - local layer2_ct = 1 - for entry in eachentrysaved(finenv.Region()) do - if entry:IsNote() then - if entry.LayerNumber == 1 then - if entry:CalcBeamedGroupEnd() then - entry.Visible = false - end - if layer1_ct % 2 == 0 then - print() - note_entry.hide_stem(entry) - end - layer1_ct = layer1_ct + 1 - elseif entry.LayerNumber == 2 then - if entry:GetBeamBeat() then - entry.Visible = false - end - if layer2_ct % 2 == 1 then - print() - note_entry.hide_stem(entry) - end - entry:SetPlayback(false) - layer2_ct = layer2_ct + 1 - end - end - end -end -- function bariolage - -bariolage() + ]]return"Bariolage","Bariolage","Bariolage: Creates alternating layer pattern from layer 1. Doesn't play nicely with odd numbered groups!"end;local o=require("library.layer")local p=require("library.note_entry")function bariolage()local q=finenv.Region()o.copy(q,1,2)local r=1;local s=1;for t in eachentrysaved(finenv.Region())do if t:IsNote()then if t.LayerNumber==1 then if t:CalcBeamedGroupEnd()then t.Visible=false end;if r%2==0 then print()p.hide_stem(t)end;r=r+1 elseif t.LayerNumber==2 then if t:GetBeamBeat()then t.Visible=false end;if s%2==1 then print()p.hide_stem(t)end;t:SetPlayback(false)s=s+1 end end end end;bariolage()end)c("library.note_entry",function(require,n,c,d)local u={}function u.finale_version(v,w,x)local y=bit32.bor(bit32.lshift(math.floor(v),24),bit32.lshift(math.floor(w),20))if x then y=bit32.bor(y,math.floor(x))end;return y end;function u.group_overlaps_region(z,q)if q:IsFullDocumentSpan()then return true end;local A=false;local B=finale.FCSystemStaves()B:LoadAllForRegion(q)for C in each(B)do if z:ContainsStaff(C:GetStaff())then A=true;break end end;if not A then return false end;if z.StartMeasure>q.EndMeasure or z.EndMeasure0 then J=true;break end;H=H+1 end;if J then local K=finale.FCStaffSystem()K:Load(I:GetFirstSystem())return finale.FCCell(K.FirstMeasure,K.TopStaff)end;local L=finale.FCMusicRegion()L:SetFullDocument()return finale.FCCell(L.EndMeasure,L.EndStaff)end;function u.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local M=finale.FCMusicRegion()M:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),M.StartStaff)end;return u.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function u.get_top_left_selected_or_visible_cell()local F=finenv.Region()if not F:IsEmpty()then return finale.FCCell(F.StartMeasure,F.StartStaff)end;return u.get_top_left_visible_cell()end;function u.is_default_measure_number_visible_on_cell(N,O,P,Q)local R=finale.FCCurrentStaffSpec()if not R:LoadForCell(O,0)then return false end;if N:GetShowOnTopStaff()and O.Staff==P.TopStaff then return true end;if N:GetShowOnBottomStaff()and O.Staff==P:CalcBottomStaff()then return true end;if R.ShowMeasureNumbers then return not N:GetExcludeOtherStaves(Q)end;return false end;function u.is_default_number_visible_and_left_aligned(N,O,S,Q,T)if N.UseScoreInfoForParts then Q=false end;if T and N:GetShowOnMultiMeasureRests(Q)then if finale.MNALIGN_LEFT~=N:GetMultiMeasureAlignment(Q)then return false end elseif O.Measure==S.FirstMeasure then if not N:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=N:GetStartAlignment(Q)then return false end else if not N:GetShowMultiples(Q)then return false end;if finale.MNALIGN_LEFT~=N:GetMultipleAlignment(Q)then return false end end;return u.is_default_measure_number_visible_on_cell(N,O,S,Q)end;function u.update_layout(U,V)U=U or 1;V=V or false;local W=finale.FCPage()if W:Load(U)then W:UpdateLayout(V)end end;function u.get_current_part()local X=finale.FCParts()X:LoadAll()return X:GetCurrent()end;function u.get_page_format_prefs()local Y=u.get_current_part()local Z=finale.FCPageFormatPrefs()local _=false;if Y:IsScore()then _=Z:LoadScore()else _=Z:LoadParts()end;return Z,_ end;function u.get_smufl_metadata_file(a0)if not a0 then a0=finale.FCFontInfo()a0:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local a1=function(a2,a0)local a3=a2 .."/SMuFL/Fonts/"..a0.Name.."/"..a0.Name..".json"return io.open(a3,"r")end;local a4=""if finenv.UI():IsOnWindows()then a4=os.getenv("LOCALAPPDATA")else a4=os.getenv("HOME").."/Library/Application Support"end;local a5=a1(a4,a0)if nil~=a5 then return a5 end;local a6="/Library/Application Support"if finenv.UI():IsOnWindows()then a6=os.getenv("COMMONPROGRAMFILES")end;return a1(a6,a0)end;function u.is_font_smufl_font(a0)if not a0 then a0=finale.FCFontInfo()a0:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=u.finale_version(27,1)then if nil~=a0.IsSMuFLFont then return a0.IsSMuFLFont end end;local a7=u.get_smufl_metadata_file(a0)if nil~=a7 then io.close(a7)return true end;return false end;function u.simple_input(a8,a9)local aa=finale.FCString()aa.LuaString=""local ab=finale.FCString()local ac=160;function format_ctrl(ad,ae,af,ag)ad:SetHeight(ae)ad:SetWidth(af)ab.LuaString=ag;ad:SetText(ab)end;title_width=string.len(a8)*6+54;if title_width>ac then ac=title_width end;text_width=string.len(a9)*6;if text_width>ac then ac=text_width end;ab.LuaString=a8;local ah=finale.FCCustomLuaWindow()ah:SetTitle(ab)local ai=ah:CreateStatic(0,0)format_ctrl(ai,16,ac,a9)local aj=ah:CreateEdit(0,20)format_ctrl(aj,20,ac,"")ah:CreateOkButton()ah:CreateCancelButton()function callback(ad)end;ah:RegisterHandleCommand(callback)if ah:ExecuteModal(nil)==finale.EXECMODAL_OK then aa.LuaString=aj:GetText(aa)return aa.LuaString end end;function u.is_finale_object(ak)return ak and type(ak)=="userdata"and ak.ClassName and ak.GetClassID and true or false end;function u.system_indent_set_to_prefs(S,Z)Z=Z or u.get_page_format_prefs()local al=finale.FCMeasure()local am=S.FirstMeasure==1;if not am and al:Load(S.FirstMeasure)then if al.ShowFullNames then am=true end end;if am and Z.UseFirstSystemMargins then S.LeftMargin=Z.FirstSystemLeft else S.LeftMargin=Z.SystemLeft end;return S:Save()end;return u end)c("library.layer",function(require,n,c,d)local u={}function u.finale_version(v,w,x)local y=bit32.bor(bit32.lshift(math.floor(v),24),bit32.lshift(math.floor(w),20))if x then y=bit32.bor(y,math.floor(x))end;return y end;function u.group_overlaps_region(z,q)if q:IsFullDocumentSpan()then return true end;local A=false;local B=finale.FCSystemStaves()B:LoadAllForRegion(q)for C in each(B)do if z:ContainsStaff(C:GetStaff())then A=true;break end end;if not A then return false end;if z.StartMeasure>q.EndMeasure or z.EndMeasure0 then J=true;break end;H=H+1 end;if J then local K=finale.FCStaffSystem()K:Load(I:GetFirstSystem())return finale.FCCell(K.FirstMeasure,K.TopStaff)end;local L=finale.FCMusicRegion()L:SetFullDocument()return finale.FCCell(L.EndMeasure,L.EndStaff)end;function u.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local M=finale.FCMusicRegion()M:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),M.StartStaff)end;return u.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function u.get_top_left_selected_or_visible_cell()local F=finenv.Region()if not F:IsEmpty()then return finale.FCCell(F.StartMeasure,F.StartStaff)end;return u.get_top_left_visible_cell()end;function u.is_default_measure_number_visible_on_cell(N,O,P,Q)local R=finale.FCCurrentStaffSpec()if not R:LoadForCell(O,0)then return false end;if N:GetShowOnTopStaff()and O.Staff==P.TopStaff then return true end;if N:GetShowOnBottomStaff()and O.Staff==P:CalcBottomStaff()then return true end;if R.ShowMeasureNumbers then return not N:GetExcludeOtherStaves(Q)end;return false end;function u.is_default_number_visible_and_left_aligned(N,O,S,Q,T)if N.UseScoreInfoForParts then Q=false end;if T and N:GetShowOnMultiMeasureRests(Q)then if finale.MNALIGN_LEFT~=N:GetMultiMeasureAlignment(Q)then return false end elseif O.Measure==S.FirstMeasure then if not N:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=N:GetStartAlignment(Q)then return false end else if not N:GetShowMultiples(Q)then return false end;if finale.MNALIGN_LEFT~=N:GetMultipleAlignment(Q)then return false end end;return u.is_default_measure_number_visible_on_cell(N,O,S,Q)end;function u.update_layout(U,V)U=U or 1;V=V or false;local W=finale.FCPage()if W:Load(U)then W:UpdateLayout(V)end end;function u.get_current_part()local X=finale.FCParts()X:LoadAll()return X:GetCurrent()end;function u.get_page_format_prefs()local Y=u.get_current_part()local Z=finale.FCPageFormatPrefs()local _=false;if Y:IsScore()then _=Z:LoadScore()else _=Z:LoadParts()end;return Z,_ end;function u.get_smufl_metadata_file(a0)if not a0 then a0=finale.FCFontInfo()a0:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local a1=function(a2,a0)local a3=a2 .."/SMuFL/Fonts/"..a0.Name.."/"..a0.Name..".json"return io.open(a3,"r")end;local a4=""if finenv.UI():IsOnWindows()then a4=os.getenv("LOCALAPPDATA")else a4=os.getenv("HOME").."/Library/Application Support"end;local a5=a1(a4,a0)if nil~=a5 then return a5 end;local a6="/Library/Application Support"if finenv.UI():IsOnWindows()then a6=os.getenv("COMMONPROGRAMFILES")end;return a1(a6,a0)end;function u.is_font_smufl_font(a0)if not a0 then a0=finale.FCFontInfo()a0:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=u.finale_version(27,1)then if nil~=a0.IsSMuFLFont then return a0.IsSMuFLFont end end;local a7=u.get_smufl_metadata_file(a0)if nil~=a7 then io.close(a7)return true end;return false end;function u.simple_input(a8,a9)local aa=finale.FCString()aa.LuaString=""local ab=finale.FCString()local ac=160;function format_ctrl(ad,ae,af,ag)ad:SetHeight(ae)ad:SetWidth(af)ab.LuaString=ag;ad:SetText(ab)end;title_width=string.len(a8)*6+54;if title_width>ac then ac=title_width end;text_width=string.len(a9)*6;if text_width>ac then ac=text_width end;ab.LuaString=a8;local ah=finale.FCCustomLuaWindow()ah:SetTitle(ab)local ai=ah:CreateStatic(0,0)format_ctrl(ai,16,ac,a9)local aj=ah:CreateEdit(0,20)format_ctrl(aj,20,ac,"")ah:CreateOkButton()ah:CreateCancelButton()function callback(ad)end;ah:RegisterHandleCommand(callback)if ah:ExecuteModal(nil)==finale.EXECMODAL_OK then aa.LuaString=aj:GetText(aa)return aa.LuaString end end;function u.is_finale_object(ak)return ak and type(ak)=="userdata"and ak.ClassName and ak.GetClassID and true or false end;function u.system_indent_set_to_prefs(S,Z)Z=Z or u.get_page_format_prefs()local al=finale.FCMeasure()local am=S.FirstMeasure==1;if not am and al:Load(S.FirstMeasure)then if al.ShowFullNames then am=true end end;if am and Z.UseFirstSystemMargins then S.LeftMargin=Z.FirstSystemLeft else S.LeftMargin=Z.SystemLeft end;return S:Save()end;return u end)return a("__root") \ No newline at end of file diff --git a/dist/note_cluster_determinate.lua b/dist/note_cluster_determinate.lua index 0823a069..1e4b861c 100644 --- a/dist/note_cluster_determinate.lua +++ b/dist/note_cluster_determinate.lua @@ -1,784 +1 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Jacob Winkler" -- With help & advice from CJ Garcia, Nick Mazuk, and Jan Angermüller. Thanks guys! - finaleplugin.Copyright = "©2019 Jacob Winkler" - finaleplugin.AuthorEmail = "jacob.winkler@mac.com" - finaleplugin.Version = "1.0" - finaleplugin.Date = "11/02/2019" - return "Cluster - Determinate", "Cluster - Determinate", "Creates a determinate cluster." -end - ---[[ -$module Note Entry -]] -- -local note_entry = {} - ---[[ -% get_music_region - -Returns an intance of `FCMusicRegion` that corresponds to the metric location of the input note entry. - -@ entry (FCNoteEntry) -: (FCMusicRegion) -]] -function note_entry.get_music_region(entry) - local exp_region = finale.FCMusicRegion() - exp_region:SetCurrentSelection() -- called to match the selected IU list (e.g., if using Staff Sets) - exp_region.StartStaff = entry.Staff - exp_region.EndStaff = entry.Staff - exp_region.StartMeasure = entry.Measure - exp_region.EndMeasure = entry.Measure - exp_region.StartMeasurePos = entry.MeasurePos - exp_region.EndMeasurePos = entry.MeasurePos - return exp_region -end - --- entry_metrics can be omitted, in which case they are constructed and released here --- return entry_metrics, loaded_here -local use_or_get_passed_in_entry_metrics = function(entry, entry_metrics) - if entry_metrics then - return entry_metrics, false - end - entry_metrics = finale.FCEntryMetrics() - if entry_metrics:Load(entry) then - return entry_metrics, true - end - return nil, false -end - ---[[ -% get_evpu_notehead_height - -Returns the calculated height of the notehead rectangle. - -@ entry (FCNoteEntry) - -: (number) the EVPU height -]] -function note_entry.get_evpu_notehead_height(entry) - local highest_note = entry:CalcHighestNote(nil) - local lowest_note = entry:CalcLowestNote(nil) - local evpu_height = (2 + highest_note:CalcStaffPosition() - lowest_note:CalcStaffPosition()) * 12 -- 12 evpu per staff step; add 2 staff steps to accommodate for notehead height at top and bottom - return evpu_height -end - ---[[ -% get_top_note_position - -Returns the vertical page coordinate of the top of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_top_note_position(entry, entry_metrics) - local retval = -math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if not entry:CalcStemUp() then - retval = entry_metrics.TopPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.BottomPosition + scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% get_bottom_note_position - -Returns the vertical page coordinate of the bottom of the notehead rectangle, not including the stem. - -@ entry (FCNoteEntry) -@ [entry_metrics] (FCEntryMetrics) entry metrics may be supplied by the caller if they are already available -: (number) -]] -function note_entry.get_bottom_note_position(entry, entry_metrics) - local retval = math.huge - local loaded_here = false - entry_metrics, loaded_here = use_or_get_passed_in_entry_metrics(entry, entry_metrics) - if nil == entry_metrics then - return retval - end - if entry:CalcStemUp() then - retval = entry_metrics.BottomPosition - else - local cell_metrics = finale.FCCell(entry.Measure, entry.Staff):CreateCellMetrics() - if nil ~= cell_metrics then - local evpu_height = note_entry.get_evpu_notehead_height(entry) - local scaled_height = math.floor(((cell_metrics.StaffScaling * evpu_height) / 10000) + 0.5) - retval = entry_metrics.TopPosition - scaled_height - cell_metrics:FreeMetrics() - end - end - if loaded_here then - entry_metrics:FreeMetrics() - end - return retval -end - ---[[ -% calc_widths - -Get the widest left-side notehead width and widest right-side notehead width. - -@ entry (FCNoteEntry) -: (number, number) widest left-side notehead width and widest right-side notehead width -]] -function note_entry.calc_widths(entry) - local left_width = 0 - local right_width = 0 - for note in each(entry) do - local note_width = note:CalcNoteheadWidth() - if note_width > 0 then - if note:CalcRightsidePlacement() then - if note_width > right_width then - right_width = note_width - end - else - if note_width > left_width then - left_width = note_width - end - end - end - end - return left_width, right_width -end - --- These functions return the offset for an expression handle. --- Expression handles are vertical when they are left-aligned --- with the primary notehead rectangle. - ---[[ -% calc_left_of_all_noteheads - -Calculates the handle offset for an expression with "Left of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_all_noteheads(entry) - if entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return -left -end - ---[[ -% calc_left_of_primary_notehead - -Calculates the handle offset for an expression with "Left of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_left_of_primary_notehead(entry) - return 0 -end - ---[[ -% calc_center_of_all_noteheads - -Calculates the handle offset for an expression with "Center of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - local width_centered = (left + right) / 2 - if not entry:CalcStemUp() then - width_centered = width_centered - left - end - return width_centered -end - ---[[ -% calc_center_of_primary_notehead - -Calculates the handle offset for an expression with "Center of Primary Notehead" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_center_of_primary_notehead(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left / 2 - end - return right / 2 -end - ---[[ -% calc_stem_offset - -Calculates the offset of the stem from the left edge of the notehead rectangle. Eventually the PDK Framework may be able to provide this instead. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset of stem from the left edge of the notehead rectangle. -]] -function note_entry.calc_stem_offset(entry) - if not entry:CalcStemUp() then - return 0 - end - local left, right = note_entry.calc_widths(entry) - return left -end - ---[[ -% calc_right_of_all_noteheads - -Calculates the handle offset for an expression with "Right of All Noteheads" horizontal positioning. - -@ entry (FCNoteEntry) the entry to calculate from -: (number) offset from left side of primary notehead rectangle -]] -function note_entry.calc_right_of_all_noteheads(entry) - local left, right = note_entry.calc_widths(entry) - if entry:CalcStemUp() then - return left + right - end - return right -end - ---[[ -% calc_note_at_index - -This function assumes `for note in each(note_entry)` always iterates in the same direction. -(Knowing how the Finale PDK works, it probably iterates from bottom to top note.) -Currently the PDK Framework does not seem to offer a better option. - -@ entry (FCNoteEntry) -@ note_index (number) the zero-based index -]] -function note_entry.calc_note_at_index(entry, note_index) - local x = 0 - for note in each(entry) do - if x == note_index then - return note - end - x = x + 1 - end - return nil -end - ---[[ -% stem_sign - -This is useful for many x,y positioning fields in Finale that mirror +/- -based on stem direction. - -@ entry (FCNoteEntry) -: (number) 1 if upstem, -1 otherwise -]] -function note_entry.stem_sign(entry) - if entry:CalcStemUp() then - return 1 - end - return -1 -end - ---[[ -% duplicate_note - -@ note (FCNote) -: (FCNote | nil) reference to added FCNote or `nil` if not success -]] -function note_entry.duplicate_note(note) - local new_note = note.Entry:AddNewNote() - if nil ~= new_note then - new_note.Displacement = note.Displacement - new_note.RaiseLower = note.RaiseLower - new_note.Tie = note.Tie - new_note.TieBackwards = note.TieBackwards - end - return new_note -end - ---[[ -% delete_note - -Removes the specified FCNote from its associated FCNoteEntry. - -@ note (FCNote) -: (boolean) true if success -]] -function note_entry.delete_note(note) - local entry = note.Entry - if nil == entry then - return false - end - - -- attempt to delete all associated entry-detail mods, but ignore any failures - finale.FCAccidentalMod():EraseAt(note) - finale.FCCrossStaffMod():EraseAt(note) - finale.FCDotMod():EraseAt(note) - finale.FCNoteheadMod():EraseAt(note) - finale.FCPercussionNoteMod():EraseAt(note) - finale.FCTablatureNoteMod():EraseAt(note) - if finale.FCTieMod then -- added in RGP Lua 0.62 - finale.FCTieMod(finale.TIEMODTYPE_TIESTART):EraseAt(note) - finale.FCTieMod(finale.TIEMODTYPE_TIEEND):EraseAt(note) - end - - return entry:DeleteNote(note) -end - ---[[ -% calc_spans_number_of_octaves - -Calculates the numer of octaves spanned by a chord (considering only staff positions, not accidentals). - -@ entry (FCNoteEntry) the entry to calculate from -: (number) of octaves spanned -]] -function note_entry.calc_spans_number_of_octaves(entry) - local top_note = entry:CalcHighestNote(nil) - local bottom_note = entry:CalcLowestNote(nil) - local displacement_diff = top_note.Displacement - bottom_note.Displacement - local num_octaves = math.ceil(displacement_diff / 7) - return num_octaves -end - ---[[ -% add_augmentation_dot - -Adds an augentation dot to the entry. This works even if the entry already has one or more augmentation dots. - -@ entry (FCNoteEntry) the entry to which to add the augmentation dot -]] -function note_entry.add_augmentation_dot(entry) - -- entry.Duration = entry.Duration | (entry.Duration >> 1) -- For Lua 5.3 and higher - entry.Duration = bit32.bor(entry.Duration, bit32.rshift(entry.Duration, 1)) -end - ---[[ -% get_next_same_v - -Returns the next entry in the same V1 or V2 as the input entry. -If the input entry is V2, only the current V2 launch is searched. -If the input entry is V1, only the current measure and layer is searched. - -@ entry (FCNoteEntry) the entry to process -: (FCNoteEntry) the next entry or `nil` in none -]] -function note_entry.get_next_same_v(entry) - local next_entry = entry:Next() - if entry.Voice2 then - if (nil ~= next_entry) and next_entry.Voice2 then - return next_entry - end - return nil - end - if entry.Voice2Launch then - while (nil ~= next_entry) and next_entry.Voice2 do - next_entry = next_entry:Next() - end - end - return next_entry -end - ---[[ -% hide_stem - -Hides the stem of the entry by replacing it with Shape 0. - -@ entry (FCNoteEntry) the entry to process -]] -function note_entry.hide_stem(entry) - local stem = finale.FCCustomStemMod() - stem:SetNoteEntry(entry) - stem:UseUpStemData(entry:CalcStemUp()) - if stem:LoadFirst() then - stem.ShapeID = 0 - stem:Save() - else - stem.ShapeID = 0 - stem:SaveNew() - end -end - - - - -local region = finenv.Region() - -local layer = {} -local layer_one_note = {} -local layer_two_note = {} - -local measure = {} - -local horizontal_offset = -20 - --- FUNCTION 1: Delete and Hide Notes -local function process_notes(music_region) - local stem_dir = {} - -- First build up a table of the initial stem direction information - for entry in eachentrysaved(region) do - entry.FreezeStem = false - table.insert(stem_dir, entry:CalcStemUp()) - end - - layer.copy(1, 2) - layer.copy(1, 3) - - local i = 1 -- To iterate stem direction table for Layer 1 - local j = 1 -- To iterate stem direction table for Layer 2 - - for note_entry in eachentrysaved(music_region) do - local span = note_entry:CalcDisplacementRange(nil) - local stem_direction = stem_dir[i] - if note_entry.LayerNumber == 1 then - stem_direction = stem_dir[i] - if note_entry:IsNote() then - if span > 2 then - delete_bottom_notes(note_entry) - else - delete_middle_notes(note_entry) - note_entry.FreezeStem = true - note_entry.StemUp = stem_direction - end - elseif note_entry:IsRest() then - note_entry:SetRestDisplacement(6) - end - if stem_direction == false and span > 2 then - hide_stems(note_entry, stem_direction) - end - i = i + 1 - elseif note_entry.LayerNumber == 2 then - stem_direction = stem_dir[j] - if note_entry:IsNote() and span > 2 then - delete_top_notes(note_entry) - else - note_entry:MakeRest() - note_entry.Visible = false - note_entry:SetRestDisplacement(4) - end - if stem_direction == true then - hide_stems(note_entry, stem_direction) - end - j = j + 1 - elseif note_entry.LayerNumber == 3 then - if note_entry:IsNote() then - for note in each(note_entry) do - note.AccidentalFreeze = true - note.Accidental = false - end - note_entry.FreezeStem = true - note_entry.StemUp = true - hide_stems(note_entry, true) - delete_top_bottom_notes(note_entry) - elseif note_entry:IsRest() then - note_entry:SetRestDisplacement(2) - end - note_entry.Visible = false - end - note_entry.CheckAccidentals = true - if note_entry:IsNote() then - n = 1 - for note in each(note_entry) do - note.NoteID = n - n = n + 1 - end - end - end -end - --- Function 2: Hide Stems (from JW's Harp Gliss script, modified) -function hide_stems(entry, stem_direction) - local stem = finale.FCCustomStemMod() - stem:SetNoteEntry(entry) - if stem_direction then -- Reverse "stemDir" - stem_direction = false - else - stem_direction = true - end - stem:UseUpStemData(stem_direction) - if stem:LoadFirst() then - stem.ShapeID = 0 - stem:Save() - else - stem.ShapeID = 0 - stem:SaveNew() - end - entry:SetBeamBeat(true) -- Since flags get hidden, use this instead of trying tro change beam width -end - --- Function 3 - Copy Layer "src" to Layer "dest" -function layer.copy(source, destination) -- source and destination layer numbers, 1 based - local region = finenv.Region() - local start = region.StartMeasure - local stop = region.EndMeasure - local system_staves = finale.FCSystemStaves() - system_staves:LoadAllForRegion(region) - - -- Set variables for 0-based layers - source = source - 1 - destination = destination - 1 - for system_staff in each(system_staves) do - local staff_number = system_staff.Staff - local note_entry_layer_source = finale.FCNoteEntryLayer(source, staff_number, start, stop) - note_entry_layer_source:Load() - local noteentrylayerDest = note_entry_layer_source:CreateCloneEntries(destination, staff_number, start) - noteentrylayerDest:Save() - noteentrylayerDest:CloneTuplets(note_entry_layer_source) - noteentrylayerDest:Save() - end -end - --- Function 4 - Delete the bottom notes, leaving only the top -function delete_bottom_notes(entry) - while entry.Count > 1 do - local lowest_note = entry:CalcLowestNote(nil) - note_entry.delete_note(lowest_note) - end -end - --- Function 5 - Delete the top notes, leaving only the bottom -function delete_top_notes(entry) - while entry.Count > 1 do - local highest_note = entry:CalcHighestNote(nil) - note_entry.delete_note(highest_note) - end -end - --- Function 6 - Delete the Top and Bottom Notes -function delete_top_bottom_notes(entry) - local highest_note = entry:CalcHighestNote(nil) - note_entry.delete_note(highest_note) - local lowest_note = entry:CalcLowestNote(nil) - note_entry.delete_note(lowest_note) -end - --- Function 6.1 - Delete the middle notes -function delete_middle_notes(entry) - while entry.Count > 2 do - local n = 1 - for note in each(entry) do - note.NoteID = n - n = n + 1 - end - for note in each(entry) do - if note.NoteID == 2 then - note_entry.delete_note(note) - end - end - end -end - --- Function 7: Create the custom line to use (or choose it if it already exists) -local function create_cluster_line() - -- Check to see if the right line exists. If it does, get the line ID - local line_exists = false - local my_line = 0 - local my_line_width = 64 * 24 * .5 -- 64 EFIXes * 24EVPUs * .5 = 1/2 space - local custom_start_line_defs = finale.FCCustomSmartLineDefs() - custom_start_line_defs:LoadAll() - for csld in each(custom_start_line_defs) do - if csld.LineStyle == finale.CUSTOMLINE_SOLID and csld.LineWidth == my_line_width then -- 1st if: Solid line, 740 - if csld.StartArrowheadStyle == finale.CLENDPOINT_NONE and csld.EndArrowheadStyle == finale.CLENDPOINT_NONE then -- 2nd if (arrowhead styles) - if csld.Horizontal == false then - my_line = csld.ItemNo - line_exists = true - end - end - end - end - - -- if the line does not exist, create it and get the line ID - if line_exists == false then - local csld = finale.FCCustomSmartLineDef() - csld.Horizontal = false - csld.LineStyle = finale.CUSTOMLINE_SOLID - csld.StartArrowheadStyle = finale.CLENDPOINT_NONE - csld.EndArrowheadStyle = finale.CLENDPOINT_NONE - csld.LineWidth = my_line_width - csld:SaveNew() - my_line = csld.ItemNo - end - return my_line -end - --- Function 7.1: Create the short-span custom line to use (or choose it if it already exists) -local function create_short_cluster_line() - -- Check to see if the right line exists. If it does, get the line ID - local line_exists = false - local my_line = 0 - local my_line_width = 64 * 24 * .333 -- 64 EFIXes * 24EVPUs * .333 = 1/3 space - local custom_smart_line_defs = finale.FCCustomSmartLineDefs() - custom_smart_line_defs:LoadAll() - for csld in each(custom_smart_line_defs) do - if csld.LineStyle == finale.CUSTOMLINE_SOLID and csld.LineWidth == my_line_width then -- 1st if: Solid line, 740 - if csld.StartArrowheadStyle == finale.CLENDPOINT_NONE and csld.EndArrowheadStyle == finale.CLENDPOINT_NONE then -- 2nd if (arrowhead styles) - if csld.Horizontal == false then - my_line = csld.ItemNo - line_exists = true - end - end - end - end - - -- if the line does not exist, create it and get the line ID - if line_exists == false then - local csld = finale.FCCustomSmartLineDef() - csld.Horizontal = false - csld.LineStyle = finale.CUSTOMLINE_SOLID - csld.StartArrowheadStyle = finale.CLENDPOINT_NONE - csld.EndArrowheadStyle = finale.CLENDPOINT_NONE - csld.LineWidth = my_line_width - csld:SaveNew() - my_line = csld.ItemNo - end - return my_line -end - --- Function 8: Attach the cluster line to the score -function add_cluster_line(left_note, right_note, line_id) - if left_note:IsNote() and left_note.Count == 1 and right_note:IsNote() then - local smartshape = finale.FCSmartShape() - local layer_one_highest = left_note:CalcHighestNote(nil) - local note_width = layer_one_highest:CalcNoteheadWidth() - local layer_one_note_y = layer_one_highest:CalcStaffPosition() - - local layer_two_highest = right_note:CalcHighestNote(nil) - local layer_two_note_y = layer_two_highest:CalcStaffPosition() - - local top_pad = 0 - local bottom_pad = 0 - if left_note.Duration >= 2048 and left_note.Duration < 4096 then -- for half notes... - top_pad = 9 - bottom_pad = top_pad - elseif left_note.Duration >= 4096 then -- for whole notes and greater... - top_pad = 10 - bottom_pad = 11.5 - end - layer_one_note_y = (layer_one_note_y * 12) - top_pad - layer_two_note_y = (layer_two_note_y * 12) + bottom_pad - - smartshape.ShapeType = finale.SMARTSHAPE_CUSTOM - smartshape.EntryBased = false - smartshape.MakeHorizontal = false - smartshape.BeatAttached = true - smartshape.PresetShape = true - smartshape.Visible = true - smartshape.LineID = line_id - - local left_segment = smartshape:GetTerminateSegmentLeft() - left_segment:SetMeasure(left_note.Measure) - left_segment:SetStaff(left_note.Staff) - left_segment:SetMeasurePos(left_note.MeasurePos) - left_segment:SetEndpointOffsetX(note_width / 2) - left_segment:SetEndpointOffsetY(layer_one_note_y) - - local right_segment = smartshape:GetTerminateSegmentRight() - right_segment:SetMeasure(right_note.Measure) - right_segment:SetStaff(right_note.Staff) - right_segment:SetMeasurePos(right_note.MeasurePos) - right_segment:SetEndpointOffsetX(note_width / 2) - right_segment:SetEndpointOffsetY(layer_two_note_y) - - smartshape:SaveNewEverything(nil, nil) - end -end - --- Function 8.1: Attach the short cluster line to the score -function add_short_cluster_line(entry, short_lineID) - if entry:IsNote() and entry.Count > 1 then - local smartshape = finale.FCSmartShape() - local left_note = entry:CalcHighestNote(nil) - local left_note_y = left_note:CalcStaffPosition() * 12 + 12 - - local right_note = entry:CalcLowestNote(nil) - local right_note_y = right_note:CalcStaffPosition() * 12 - 12 - - smartshape.ShapeType = finale.SMARTSHAPE_CUSTOM - smartshape.EntryBased = false - smartshape.MakeHorizontal = false - smartshape.PresetShape = true - smartshape.Visible = true - smartshape.BeatAttached = true - smartshape.LineID = short_lineID - - local left_segment = smartshape:GetTerminateSegmentLeft() - left_segment:SetMeasure(entry.Measure) - left_segment:SetStaff(entry.Staff) - left_segment:SetMeasurePos(entry.MeasurePos) - left_segment:SetEndpointOffsetX(horizontal_offset) - left_segment:SetEndpointOffsetY(left_note_y) - - local right_segment = smartshape:GetTerminateSegmentRight() - right_segment:SetMeasure(entry.Measure) - right_segment:SetStaff(entry.Staff) - right_segment:SetMeasurePos(entry.MeasurePos) - right_segment:SetEndpointOffsetX(horizontal_offset) - right_segment:SetEndpointOffsetY(right_note_y) - - smartshape:SaveNewEverything(nil, nil) - end -end - -local line_id = create_cluster_line() -local short_lineID = create_short_cluster_line() - -for add_staff = region:GetStartStaff(), region:GetEndStaff() do - local count = 0 - - for k in pairs(layer_one_note) do - layer_one_note[k] = nil - end - for k in pairs(layer_two_note) do - layer_two_note[k] = nil - end - for k in pairs(measure) do - measure[k] = nil - end - - region:SetStartStaff(add_staff) - region:SetEndStaff(add_staff) - local measures = finale.FCMeasures() - measures:LoadRegion(region) - process_notes(region) - - for entry in eachentrysaved(region) do - if entry.LayerNumber == 1 then - table.insert(layer_one_note, entry) - table.insert(measure, entry.Measure) - count = count + 1 - elseif entry.LayerNumber == 2 then - table.insert(layer_two_note, entry) - end - end - - for i = 1, count do - add_short_cluster_line(layer_one_note[i], short_lineID) - add_cluster_line(layer_one_note[i], layer_two_note[i], line_id) - end -end - --- separate move accidentals function for short clusters that encompass a 3rd -for note_entry in eachentrysaved(finenv.Region()) do - if note_entry:IsNote() and note_entry.Count > 1 then - for note in each(note_entry) do - if note.Accidental == true then - local accidental_mod = finale.FCAccidentalMod() - accidental_mod:SetNoteEntry(note_entry) - accidental_mod:SetUseCustomVerticalPos(true) - accidental_mod:SetHorizontalPos(horizontal_offset * 1.5) - accidental_mod:SaveAt(note) - end - end - end -end +local a,b,c,d=(function(e)local f={[{}]=true}local g;local h={}local require;local i={}g=function(j,k)if not h[j]then h[j]=k end end;require=function(j)local l=i[j]if l then if l==f then return nil end else if not h[j]then if not e then local m=type(j)=='string'and'\"'..j..'\"'or tostring(j)error('Tried to require '..m..', but no such module has been registered')else return e(j)end end;i[j]=f;l=h[j](require,i,g,h)i[j]=l end;return l end;return require,i,g,h end)(require)c("__root",function(require,o,c,d)function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Jacob Winkler"finaleplugin.Copyright="©2019 Jacob Winkler"finaleplugin.AuthorEmail="jacob.winkler@mac.com"finaleplugin.Version="1.0"finaleplugin.Date="11/02/2019"return"Cluster - Determinate","Cluster - Determinate","Creates a determinate cluster."end;local p=require("library.note_entry")local q=finenv.Region()local r={}local s={}local t={}local u={}local v=-20;local function w(x)local y={}for z in eachentrysaved(q)do z.FreezeStem=false;table.insert(y,z:CalcStemUp())end;r.copy(1,2)r.copy(1,3)local A=1;local B=1;for p in eachentrysaved(x)do local C=p:CalcDisplacementRange(nil)local D=y[A]if p.LayerNumber==1 then D=y[A]if p:IsNote()then if C>2 then delete_bottom_notes(p)else delete_middle_notes(p)p.FreezeStem=true;p.StemUp=D end elseif p:IsRest()then p:SetRestDisplacement(6)end;if D==false and C>2 then hide_stems(p,D)end;A=A+1 elseif p.LayerNumber==2 then D=y[B]if p:IsNote()and C>2 then delete_top_notes(p)else p:MakeRest()p.Visible=false;p:SetRestDisplacement(4)end;if D==true then hide_stems(p,D)end;B=B+1 elseif p.LayerNumber==3 then if p:IsNote()then for E in each(p)do E.AccidentalFreeze=true;E.Accidental=false end;p.FreezeStem=true;p.StemUp=true;hide_stems(p,true)delete_top_bottom_notes(p)elseif p:IsRest()then p:SetRestDisplacement(2)end;p.Visible=false end;p.CheckAccidentals=true;if p:IsNote()then n=1;for E in each(p)do E.NoteID=n;n=n+1 end end end end;function hide_stems(z,D)local F=finale.FCCustomStemMod()F:SetNoteEntry(z)if D then D=false else D=true end;F:UseUpStemData(D)if F:LoadFirst()then F.ShapeID=0;F:Save()else F.ShapeID=0;F:SaveNew()end;z:SetBeamBeat(true)end;function r.copy(G,H)local q=finenv.Region()local I=q.StartMeasure;local J=q.EndMeasure;local K=finale.FCSystemStaves()K:LoadAllForRegion(q)G=G-1;H=H-1;for L in each(K)do local M=L.Staff;local N=finale.FCNoteEntryLayer(G,M,I,J)N:Load()local O=N:CreateCloneEntries(H,M,I)O:Save()O:CloneTuplets(N)O:Save()end end;function delete_bottom_notes(z)while z.Count>1 do local P=z:CalcLowestNote(nil)p.delete_note(P)end end;function delete_top_notes(z)while z.Count>1 do local Q=z:CalcHighestNote(nil)p.delete_note(Q)end end;function delete_top_bottom_notes(z)local Q=z:CalcHighestNote(nil)p.delete_note(Q)local P=z:CalcLowestNote(nil)p.delete_note(P)end;function delete_middle_notes(z)while z.Count>2 do local n=1;for E in each(z)do E.NoteID=n;n=n+1 end;for E in each(z)do if E.NoteID==2 then p.delete_note(E)end end end end;local function R()local S=false;local T=0;local U=64*24*.5;local V=finale.FCCustomSmartLineDefs()V:LoadAll()for W in each(V)do if W.LineStyle==finale.CUSTOMLINE_SOLID and W.LineWidth==U then if W.StartArrowheadStyle==finale.CLENDPOINT_NONE and W.EndArrowheadStyle==finale.CLENDPOINT_NONE then if W.Horizontal==false then T=W.ItemNo;S=true end end end end;if S==false then local W=finale.FCCustomSmartLineDef()W.Horizontal=false;W.LineStyle=finale.CUSTOMLINE_SOLID;W.StartArrowheadStyle=finale.CLENDPOINT_NONE;W.EndArrowheadStyle=finale.CLENDPOINT_NONE;W.LineWidth=U;W:SaveNew()T=W.ItemNo end;return T end;local function X()local S=false;local T=0;local U=64*24*.333;local Y=finale.FCCustomSmartLineDefs()Y:LoadAll()for W in each(Y)do if W.LineStyle==finale.CUSTOMLINE_SOLID and W.LineWidth==U then if W.StartArrowheadStyle==finale.CLENDPOINT_NONE and W.EndArrowheadStyle==finale.CLENDPOINT_NONE then if W.Horizontal==false then T=W.ItemNo;S=true end end end end;if S==false then local W=finale.FCCustomSmartLineDef()W.Horizontal=false;W.LineStyle=finale.CUSTOMLINE_SOLID;W.StartArrowheadStyle=finale.CLENDPOINT_NONE;W.EndArrowheadStyle=finale.CLENDPOINT_NONE;W.LineWidth=U;W:SaveNew()T=W.ItemNo end;return T end;function add_cluster_line(Z,_,a0)if Z:IsNote()and Z.Count==1 and _:IsNote()then local a1=finale.FCSmartShape()local a2=Z:CalcHighestNote(nil)local a3=a2:CalcNoteheadWidth()local a4=a2:CalcStaffPosition()local a5=_:CalcHighestNote(nil)local a6=a5:CalcStaffPosition()local a7=0;local a8=0;if Z.Duration>=2048 and Z.Duration<4096 then a7=9;a8=a7 elseif Z.Duration>=4096 then a7=10;a8=11.5 end;a4=a4*12-a7;a6=a6*12+a8;a1.ShapeType=finale.SMARTSHAPE_CUSTOM;a1.EntryBased=false;a1.MakeHorizontal=false;a1.BeatAttached=true;a1.PresetShape=true;a1.Visible=true;a1.LineID=a0;local a9=a1:GetTerminateSegmentLeft()a9:SetMeasure(Z.Measure)a9:SetStaff(Z.Staff)a9:SetMeasurePos(Z.MeasurePos)a9:SetEndpointOffsetX(a3/2)a9:SetEndpointOffsetY(a4)local aa=a1:GetTerminateSegmentRight()aa:SetMeasure(_.Measure)aa:SetStaff(_.Staff)aa:SetMeasurePos(_.MeasurePos)aa:SetEndpointOffsetX(a3/2)aa:SetEndpointOffsetY(a6)a1:SaveNewEverything(nil,nil)end end;function add_short_cluster_line(z,ab)if z:IsNote()and z.Count>1 then local a1=finale.FCSmartShape()local Z=z:CalcHighestNote(nil)local ac=Z:CalcStaffPosition()*12+12;local _=z:CalcLowestNote(nil)local ad=_:CalcStaffPosition()*12-12;a1.ShapeType=finale.SMARTSHAPE_CUSTOM;a1.EntryBased=false;a1.MakeHorizontal=false;a1.PresetShape=true;a1.Visible=true;a1.BeatAttached=true;a1.LineID=ab;local a9=a1:GetTerminateSegmentLeft()a9:SetMeasure(z.Measure)a9:SetStaff(z.Staff)a9:SetMeasurePos(z.MeasurePos)a9:SetEndpointOffsetX(v)a9:SetEndpointOffsetY(ac)local aa=a1:GetTerminateSegmentRight()aa:SetMeasure(z.Measure)aa:SetStaff(z.Staff)aa:SetMeasurePos(z.MeasurePos)aa:SetEndpointOffsetX(v)aa:SetEndpointOffsetY(ad)a1:SaveNewEverything(nil,nil)end end;local a0=R()local ab=X()for ae=q:GetStartStaff(),q:GetEndStaff()do local af=0;for ag in pairs(s)do s[ag]=nil end;for ag in pairs(t)do t[ag]=nil end;for ag in pairs(u)do u[ag]=nil end;q:SetStartStaff(ae)q:SetEndStaff(ae)local ah=finale.FCMeasures()ah:LoadRegion(q)w(q)for z in eachentrysaved(q)do if z.LayerNumber==1 then table.insert(s,z)table.insert(u,z.Measure)af=af+1 elseif z.LayerNumber==2 then table.insert(t,z)end end;for A=1,af do add_short_cluster_line(s[A],ab)add_cluster_line(s[A],t[A],a0)end end;for p in eachentrysaved(finenv.Region())do if p:IsNote()and p.Count>1 then for E in each(p)do if E.Accidental==true then local ai=finale.FCAccidentalMod()ai:SetNoteEntry(p)ai:SetUseCustomVerticalPos(true)ai:SetHorizontalPos(v*1.5)ai:SaveAt(E)end end end end end)c("library.note_entry",function(require,o,c,d)local aj={}function aj.finale_version(ak,al,am)local an=bit32.bor(bit32.lshift(math.floor(ak),24),bit32.lshift(math.floor(al),20))if am then an=bit32.bor(an,math.floor(am))end;return an end;function aj.group_overlaps_region(ao,q)if q:IsFullDocumentSpan()then return true end;local ap=false;local aq=finale.FCSystemStaves()aq:LoadAllForRegion(q)for ar in each(aq)do if ao:ContainsStaff(ar:GetStaff())then ap=true;break end end;if not ap then return false end;if ao.StartMeasure>q.EndMeasure or ao.EndMeasure0 then ay=true;break end;aw=aw+1 end;if ay then local az=finale.FCStaffSystem()az:Load(ax:GetFirstSystem())return finale.FCCell(az.FirstMeasure,az.TopStaff)end;local aA=finale.FCMusicRegion()aA:SetFullDocument()return finale.FCCell(aA.EndMeasure,aA.EndStaff)end;function aj.get_top_left_visible_cell()if not finenv.UI():IsPageView()then local aB=finale.FCMusicRegion()aB:SetFullDocument()return finale.FCCell(finenv.UI():GetCurrentMeasure(),aB.StartStaff)end;return aj.get_first_cell_on_or_after_page(finenv.UI():GetCurrentPage())end;function aj.get_top_left_selected_or_visible_cell()local au=finenv.Region()if not au:IsEmpty()then return finale.FCCell(au.StartMeasure,au.StartStaff)end;return aj.get_top_left_visible_cell()end;function aj.is_default_measure_number_visible_on_cell(aC,aD,aE,aF)local aG=finale.FCCurrentStaffSpec()if not aG:LoadForCell(aD,0)then return false end;if aC:GetShowOnTopStaff()and aD.Staff==aE.TopStaff then return true end;if aC:GetShowOnBottomStaff()and aD.Staff==aE:CalcBottomStaff()then return true end;if aG.ShowMeasureNumbers then return not aC:GetExcludeOtherStaves(aF)end;return false end;function aj.is_default_number_visible_and_left_aligned(aC,aD,aH,aF,aI)if aC.UseScoreInfoForParts then aF=false end;if aI and aC:GetShowOnMultiMeasureRests(aF)then if finale.MNALIGN_LEFT~=aC:GetMultiMeasureAlignment(aF)then return false end elseif aD.Measure==aH.FirstMeasure then if not aC:GetShowOnSystemStart()then return false end;if finale.MNALIGN_LEFT~=aC:GetStartAlignment(aF)then return false end else if not aC:GetShowMultiples(aF)then return false end;if finale.MNALIGN_LEFT~=aC:GetMultipleAlignment(aF)then return false end end;return aj.is_default_measure_number_visible_on_cell(aC,aD,aH,aF)end;function aj.update_layout(aJ,aK)aJ=aJ or 1;aK=aK or false;local aL=finale.FCPage()if aL:Load(aJ)then aL:UpdateLayout(aK)end end;function aj.get_current_part()local aM=finale.FCParts()aM:LoadAll()return aM:GetCurrent()end;function aj.get_page_format_prefs()local aN=aj.get_current_part()local aO=finale.FCPageFormatPrefs()local aP=false;if aN:IsScore()then aP=aO:LoadScore()else aP=aO:LoadParts()end;return aO,aP end;function aj.get_smufl_metadata_file(aQ)if not aQ then aQ=finale.FCFontInfo()aQ:LoadFontPrefs(finale.FONTPREF_MUSIC)end;local aR=function(aS,aQ)local aT=aS.."/SMuFL/Fonts/"..aQ.Name.."/"..aQ.Name..".json"return io.open(aT,"r")end;local aU=""if finenv.UI():IsOnWindows()then aU=os.getenv("LOCALAPPDATA")else aU=os.getenv("HOME").."/Library/Application Support"end;local aV=aR(aU,aQ)if nil~=aV then return aV end;local aW="/Library/Application Support"if finenv.UI():IsOnWindows()then aW=os.getenv("COMMONPROGRAMFILES")end;return aR(aW,aQ)end;function aj.is_font_smufl_font(aQ)if not aQ then aQ=finale.FCFontInfo()aQ:LoadFontPrefs(finale.FONTPREF_MUSIC)end;if finenv.RawFinaleVersion>=aj.finale_version(27,1)then if nil~=aQ.IsSMuFLFont then return aQ.IsSMuFLFont end end;local aX=aj.get_smufl_metadata_file(aQ)if nil~=aX then io.close(aX)return true end;return false end;function aj.simple_input(aY,aZ)local a_=finale.FCString()a_.LuaString=""local b0=finale.FCString()local b1=160;function format_ctrl(b2,b3,b4,b5)b2:SetHeight(b3)b2:SetWidth(b4)b0.LuaString=b5;b2:SetText(b0)end;title_width=string.len(aY)*6+54;if title_width>b1 then b1=title_width end;text_width=string.len(aZ)*6;if text_width>b1 then b1=text_width end;b0.LuaString=aY;local b6=finale.FCCustomLuaWindow()b6:SetTitle(b0)local b7=b6:CreateStatic(0,0)format_ctrl(b7,16,b1,aZ)local b8=b6:CreateEdit(0,20)format_ctrl(b8,20,b1,"")b6:CreateOkButton()b6:CreateCancelButton()function callback(b2)end;b6:RegisterHandleCommand(callback)if b6:ExecuteModal(nil)==finale.EXECMODAL_OK then a_.LuaString=b8:GetText(a_)return a_.LuaString end end;function aj.is_finale_object(b9)return b9 and type(b9)=="userdata"and b9.ClassName and b9.GetClassID and true or false end;function aj.system_indent_set_to_prefs(aH,aO)aO=aO or aj.get_page_format_prefs()local ba=finale.FCMeasure()local bb=aH.FirstMeasure==1;if not bb and ba:Load(aH.FirstMeasure)then if ba.ShowFullNames then bb=true end end;if bb and aO.UseFirstSystemMargins then aH.LeftMargin=aO.FirstSystemLeft else aH.LeftMargin=aO.SystemLeft end;return aH:Save()end;return aj end)return a("__root") \ No newline at end of file diff --git a/dist/note_cluster_indeterminate.lua b/dist/note_cluster_indeterminate.lua index 5e87d2ab..02324e76 100644 --- a/dist/note_cluster_indeterminate.lua +++ b/dist/note_cluster_indeterminate.lua @@ -1,137 +1 @@ -function plugindef() - finaleplugin.RequireSelection = true - finaleplugin.Author = "Jacob Winkler" -- With help & advice from CJ Garcia, Nick Mazuk, and Jan Angermüller. Thanks guys! - finaleplugin.Copyright = "©2019 Jacob Winkler" - finaleplugin.AuthorEmail = "jacob.winkler@mac.com" - finaleplugin.Version = "1.0" - finaleplugin.Date = "11/02/2019" - return "Cluster - Indeterminate", "Cluster - Indeterminate", "Creates Indeterminate Clusters" -end - -local distance_preferences = finale.FCDistancePrefs() -distance_preferences:Load(1) -local size_preferences = finale.FCSizePrefs() -size_preferences:Load(1) -local stem_thickness = size_preferences.StemLineThickness -- stem thickness in EFIXes -stem_thickness = stem_thickness / 64 -- This converts EFIX to EVPU - -function cluster_indeterminate() - for note_entry in eachentrysaved(finenv.Region()) do - if note_entry:IsNote() and note_entry.Count > 1 then - local max = note_entry.Count - local n = 1 - local dot = finale.FCDotMod() - local lowest_note = note_entry:CalcLowestNote(nil) - local lowest_note_pos = lowest_note:CalcStaffPosition() - local low_space = lowest_note_pos % 2 -- 0 if lowest note is on a line, 1 if on a space - local low_span = 0 - local adjust_dots = false - - -- Quick calculation to see if dots need to move... - local i = 1 - for note in each(note_entry) do - local stem_direction = note_entry:CalcStemUp() - local right_side = note:CalcRightsidePlacement() - if (stem_direction == true and right_side == true) then - adjust_dots = true - end - if i == 2 then - low_span = note:CalcStaffPosition() - lowest_note_pos - end - i = i + 1 - end - -- Process the notes - for note in each(note_entry) do - local stem_direction = note_entry:CalcStemUp() - local notehead = finale.FCNoteheadMod() - notehead:EraseAt(note) - notehead:SetUseCustomFont(true) - notehead.FontName = "Engraver Font Set" - local notehead_offset = 35 -- 35 is a good value for quarter/half notes in Maestro font. Whole notes need to be larger... - local rightside = note:CalcRightsidePlacement() - - -- Thank you to CJ Garcia for the duration logic! - - if note_entry.Duration < 2048 then -- for notes less than half notes - notehead.CustomChar = 242 - -- For stems up = true, notes to the right are 'secondary' --> move them to the left - if stem_direction == true and rightside == true then - notehead.HorizontalPos = -notehead_offset - end - -- For stems down, notes to the left are 'secondary' --> move them to the right - if stem_direction == false and rightside == false then - notehead.HorizontalPos = notehead_offset - end - end - - if (note_entry.Duration >= 2048) and (note_entry.Duration < 4096) then -- for half notes - if n == 1 then - notehead.CustomChar = 201 - elseif n == max then - notehead.CustomChar = 59 - else - notehead.CustomChar = 58 - end - -- For stems up = true, notes to the right are 'secondary' --> move them to the left - if stem_direction == true and rightside == true then - notehead.HorizontalPos = -notehead_offset - end - -- For stems down, notes to the left are 'secondary' --> move them to the right - if stem_direction == false and rightside == false then - notehead.HorizontalPos = notehead_offset - end - end - - if (note_entry.Duration >= 4096) then -- for whole notes - if n == 1 then - notehead.CustomChar = 201 - elseif n == max then - notehead.CustomChar = 59 - else - notehead.CustomChar = 58 - end - notehead_offset = 32 -- Slightly lower value for whole note... Maybe because no stem? - if stem_direction == true and rightside == true then - notehead.HorizontalPos = -notehead_offset - end - -- For stems down, notes to the left are 'secondary' --> move them to the right - if stem_direction == false and rightside == false then - notehead.HorizontalPos = notehead_offset - end - end - - if n > 1 and n < max then -- for all inner notes, set ties to off - note.Tie = false - end - - -- - if note_entry:IsDotted() then - local horizontal = 0 - if adjust_dots == true then - horizontal = -notehead_offset - end - if n == 1 and low_span <= 1 and low_space == 1 then - dot.VerticalPos = 24 - elseif n > 1 and n < max then - -- 10000 is enough to move the dots off any page - dot.VerticalPos = 10000 - dot.HorizontalPos = 10000 - else - dot.VerticalPos = 0 - end - dot.HorizontalPos = horizontal - dot:SaveAt(note) - end - - note.AccidentalFreeze = true - note.Accidental = false - notehead:SaveAt(note) - - n = n + 1 - end - note_entry.LedgerLines = false - end - end -end - -cluster_indeterminate() +function plugindef()finaleplugin.RequireSelection=true;finaleplugin.Author="Jacob Winkler"finaleplugin.Copyright="©2019 Jacob Winkler"finaleplugin.AuthorEmail="jacob.winkler@mac.com"finaleplugin.Version="1.0"finaleplugin.Date="11/02/2019"return"Cluster - Indeterminate","Cluster - Indeterminate","Creates Indeterminate Clusters"end;local a=finale.FCDistancePrefs()a:Load(1)local b=finale.FCSizePrefs()b:Load(1)local c=b.StemLineThickness;c=c/64;function cluster_indeterminate()for d in eachentrysaved(finenv.Region())do if d:IsNote()and d.Count>1 then local e=d.Count;local f=1;local g=finale.FCDotMod()local h=d:CalcLowestNote(nil)local i=h:CalcStaffPosition()local j=i%2;local k=0;local l=false;local m=1;for n in each(d)do local o=d:CalcStemUp()local p=n:CalcRightsidePlacement()if o==true and p==true then l=true end;if m==2 then k=n:CalcStaffPosition()-i end;m=m+1 end;for n in each(d)do local o=d:CalcStemUp()local q=finale.FCNoteheadMod()q:EraseAt(n)q:SetUseCustomFont(true)q.FontName="Engraver Font Set"local r=35;local s=n:CalcRightsidePlacement()if d.Duration<2048 then q.CustomChar=242;if o==true and s==true then q.HorizontalPos=-r end;if o==false and s==false then q.HorizontalPos=r end end;if d.Duration>=2048 and d.Duration<4096 then if f==1 then q.CustomChar=201 elseif f==e then q.CustomChar=59 else q.CustomChar=58 end;if o==true and s==true then q.HorizontalPos=-r end;if o==false and s==false then q.HorizontalPos=r end end;if d.Duration>=4096 then if f==1 then q.CustomChar=201 elseif f==e then q.CustomChar=59 else q.CustomChar=58 end;r=32;if o==true and s==true then q.HorizontalPos=-r end;if o==false and s==false then q.HorizontalPos=r end end;if f>1 and f1 and f