{ "version": 3, "sources": ["../../app/cyphrme.js", "../../app/ajax.js", "../../app/paginate.js", "../../app/comment.js", "../../app/error_enum.js", "../../app/wallet.js", "../../app/login.js", "../../app/dragster.js", "../../app/file.js", "../../app/ac_options.js", "../../app/cva.js", "../../app/bundle.js", "../../app/qr.js", "../../app/label.js", "../../app/table.js"], "sourcesContent": ["\"use strict\";\n\nimport * as Lib from '../lib/lib~fv=LNbMt1n5.min.js';\n\n/**\n@typedef {import('../../../pkg/cozejs/key.js').CozeKey} CozeKey\n **/\n\nexport {\n\tSite,\n\tAPI,\n\tCyphrmeLogoLong,\n\tPage,\n\tMaxFileSize,\n\tQRCodeURL,\n\n\tLog,\n\n\tAddOnload,\n\tAddOnloadFirst,\n\tRemoveOnload,\n\n\tParseSerializedCozeKey,\n\n\tIsCyphrmeDigest,\n\tIsHex,\n\tIsBASE37,\n\tSetLogLevel,\n\tUTKtoHR,\n\tUnixToHR,\n\n\tError,\n\tNotification,\n\n\tJSONPretty,\n\tJumpToAnchor,\n\tCollapse,\n};\n\n// OnLoad functions which are executed on page load. \nvar OnLoads = [];\n\n////////////////////////////////////////////////////////////////////////////////\n// Globals\n////////////////////////////////////////////////////////////////////////////////\n\nconst Site = window.location.origin;\nconst ApiURI = \"/api/v1\"; // JSON api\nconst BApiURI = ApiURI + \"/b\"; // Binary api\n// Cyphrme logo long\nconst CyphrmeLogoLong = \"/assets/img/cyphrme_long_500x135.png\"\nconst MaxFileSize = 30000000; // 30 mb / 30 million bytes.\nconst QRCodeURL = \"HTTPS://CYPHR.ME/AC/\"\n\n\n// Internal CRUUD logic: Create Read Upsert Update Delete. \n//\n// Create is creation. If the entity exists, it must error. Example:\n// \"/image/create\" \n//\n// Upsert should be used to add new or edit an existing entity. Does not error\n// if already exists.\n//\n// Update updates an existing record and must error on no existing record. \n//\n// Read is a get and is the default/implicit. Example: \"/image\"\n//\n// Delete Example: \"/image/delete\"\n\n// API is ordered alphabetically\nconst API = {\n\tPost: {\n\t\t// Post methods\n\t\tAnticounterfeits: ApiURI + \"/ac/create\",\n\t\tACPageUpdate: ApiURI + \"/ac/update\",\n\t\tACPagesUpdate: ApiURI + \"/acs/update\", // Variadic/batch updates\n\t\tACsLatestCoze: ApiURI + \"/acs/coze/read\", // Batch read for array of ACs id, to pull latest everythings.\n\t\tPJCreate: ApiURI + \"/pj/create\",\n\t\tPJUpdate: ApiURI + \"/pj/update\",\n\t\tClearCookies: ApiURI + \"/cookies/clear\",\n\t\tComment: ApiURI + \"/comment/create\",\n\t\tCommentDelete: ApiURI + \"/comment/delete\",\n\t\tCommentUpdate: ApiURI + \"/comment/update\",\n\t\tEmailCreate: ApiURI + \"/user/email/create\", // For pre-invite user account creation.\n\t\tEmailPrivateKeyBackup: ApiURI + \"/user/email/backup/create\", // For emailing backup of private key.\n\t\tFile: ApiURI + \"/file/create\",\n\t\tFileDelete: ApiURI + \"/file/delete\", // Alias. Presumed to be a single file in an Array.\n\t\tFilesDelete: ApiURI + \"/file/delete\",\n\t\tImageDelete: ApiURI + \"/file/delete\", // Alias. Also presumed to be an image w/ thumbnail.\n\t\tKeyUploaded: ApiURI + \"/key/read\", // POST request, Read endpoint. Accepts slice of tmbs.\n\t\tUpsertKey: ApiURI + \"/key/upsert\",\n\t\tAddNewUser: ApiURI + \"/user/invite/create\",\n\t\tCancelNewUser: ApiURI + \"/user/invite/delete\",\n\t\tCancelNewUsers: ApiURI + \"/users/invite/delete\", // variadic\n\t\tDeleteKey: ApiURI + \"/key/delete\",\n\t\tDeleteKeys: ApiURI + \"/keys/delete\", // variadic\n\t\tRevokeKey: ApiURI + \"/key/revoke\",\n\t\tRevokeKeyOther: ApiURI + \"/key/other/revoke\",\n\t\tLogin: ApiURI + '/login/create',\n\t\tProfile: ApiURI + \"/user/profile/update\",\n\t\tProfilePicture: ApiURI + \"/user/profile/picture/update\",\n\t\tProfilePage: ApiURI + \"/profile\", // Read\n\t\tPurchaseOrder: ApiURI + \"/user/email/purchaseRequest/create\",\n\t\tReportCounterfeitUrI: ApiURI + \"/report/create\",\n\t\tCozeUpload: ApiURI + \"/coze/upload\",\n\t\tEmailVerify: ApiURI + \"/user/email/verify/create\"\n\t},\n\tGet: {\n\t\t// Get Methods for JSON\n\t\tSearch: ApiURI + \"/s\", // Search endpoint for the application.\n\t\tSearchCoze: ApiURI + \"/s/coze\", // Search endpoint for pulling the record's Coze the application.\n\t\tCoze: ApiURI + \"/coze\", // Gets a Coze for a given ID.\n\t\tPJJSON: ApiURI + \"/pj\", // Gets JSON for PJ.\n\t\tACJSON: ApiURI + \"/ac\", // Gets JSON for AC.\n\t\tAcpage: ApiURI + \"/acpage\", // Gets a acpage for a given ID, and all associated attributes (images, reviews, rating, etc.)\n\t\tACPageInfo: ApiURI + \"/acpage_details\", // Gets the details for a given acpage. This includes actions and inheritance of other ac's.\n\t\tPageHit: ApiURI + \"/pagehit\", // Although Get request, performs Post like actions for endpoint and adds page hit to the page.\n\t\tKey: ApiURI + \"/key\", // Single KeyStore (not cozekey) by tmb. e.g. api/v1/key/cLj8vsYtMBwYkzoFVZHBZo6SNL8wSdCIjCKAwXNuhOk \n\t\tCozeKey: ApiURI + \"/cozekey\", // Single Coze key by tmb. e.g. api/v1/cozekey/cLj8vsYtMBwYkzoFVZHBZo6SNL8wSdCIjCKAwXNuhOk\n\t\tImage: BApiURI + \"/image\", // Binary Read endpoint.\n\t\tFile: BApiURI + \"/file\", // Binary Read endpoint.\n\t\tImageMeta: ApiURI + \"/images\", // Returns Image Meta\n\t\tInviteList: ApiURI + \"/invite\", // Gets a list of invites for a given account.\n\t\tDisplayName: ApiURI + \"/display_name\", // Gets the current display name for a uad\n\t\tEmailVerify: ApiURI + \"/user/email/verify\", // Where verify email system coze is sent as the url param.\n\t\tUser: ApiURI + \"/user\", // Gets the user.\n\t\tProfile: ApiURI + \"/profile\", // Gets the account/profile details for a given root thumbprint. If no associated account exists, The page reports profile is either unverified, or needs to be created. \n\t\tUserActions: ApiURI + \"/user/actions\", // Gets actions for a given user\n\t\tUserKeys: ApiURI + \"/user/keys\", // Gets the public keys for a given User's AccountID\n\t\tUserItems: ApiURI + \"/user/items\", // Gets items for a given user\n\t\tUserModels: ApiURI + \"/user/models\", // Gets models for a given user\n\t\tUserComments: ApiURI + \"/user/comments\", // Gets comments for a given user\n\t\tUserImages: ApiURI + \"/user/images\", // Gets images for a given user\n\t\tUserFiles: ApiURI + \"/user/files\", // Gets files for a given user\n\t}\n};\n\n// Page is for HTML pages.\nconst Page = {\n\tAccount: \"/account\",\n\tActions: \"/user/actions\", // Account log for a given User. \n\tComments: \"/comments\", // Pagination page for comments on a certain pager.\n\tCozeKey: \"/cozekey\", // Coze key, not key store. There is no html KeyStore page (use json for keystore).\n\tEverything: \"/e\", // Everything page. \n\tImages: \"/images\", // Pagination page for images on a certain pager.\n\tPreInviteAccountModal: \"/preinvite_account_modal\", // Returns the HTML modal.\n\tPublicProfile: \"/user/id\", // Public profile. \n\tProfile: \"/profile\", // Public profile. Append \"/ID\"\n\tUserKeys: \"/user/keys\", // User's active keys. Append \"/ID\". \n\tUserRevokedKeys: \"/user/keys/revoked\", // User's active keys. Append \"/ID\". \n\tUserComments: \"/user/comments\",\n\tUserImages: \"/user/images\",\n\tUserFiles: \"/user/files\",\n\tVerify: \"/coze\", // Coze Verifier. \n};\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Third Party Tools\n////////////////////////////////////////////////////////////////////////////////\nconst RecaptchaPublic = \"6Ldn1zYaAAAAAOD8XTJ3w7jd9O5SQcBWggzWnF8o\";\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Functions\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nAll pages ready is called on page load.\nShould be called on \"DOMContentLoaded\", and only executes once.\n@returns {void}\n */\nasync function AllPagesReady() {\n\tconsole.log(\"Executing AllPagesReady\");\n\t// Execute all onloads.\n\t// console.log(\"Iterating over OnLoads length: \" + OnLoads.length);\n\t// console.log(OnLoads);\n\tAddOnload(OnloadLast);\n\tfor (var i = 0; i < OnLoads.length; i++) {\n\t\tawait OnLoads[i]();\n\t}\n};\n\n/**\nRemoveOnload removes a function from Onloads. \n@param {function} func \n@returns {void}\n */\nfunction RemoveOnload(func) {\n\tOnLoads.splice(OnLoads.indexOf(func), 1);\n};\n\n/**\nAddOnload append a function to Onloads at the bottom. \n@param {function} func \n@returns {void}\n */\nfunction AddOnload(func) {\n\tOnLoads.push(func);\n};\n\n/**\nAddOnloadFirst prepends a function to Onloads at the top. \n@param {function} func \n@returns {void}\n */\nfunction AddOnloadFirst(func) {\n\tOnLoads.unshift(func);\n};\n\n// Javascript for site wide (Cyphr.me) functions.\ndocument.addEventListener('DOMContentLoaded', () => {\n\tAllPagesReady();\n});\n\n\nfunction OnloadLast() {\n\t// If an anchor exists in the URL, it scrolls the element into view.\n\tJumpToAnchor();\n\tsetCozeJSONLink();\n\tJSONPretty();\n\t// TODO global copy.\n}\n\n// setCozeJSONLink selects all `coze_json_links`, and sets the verify link.\n// Assumes 'coze_json_link' and 'coze_json' are encapsulated in a parent element.\nfunction setCozeJSONLink() {\n\tdocument.querySelectorAll('.coze_json_link').forEach((item) => {\n\t\titem.href = Page.Verify + \"?verify&input=\" +\n\t\t\tJSON.stringify(JSON.parse(item.parentElement.querySelector('.coze_json').textContent)) +\n\t\t\t\"&verify\";\n\t});\n}\n\n//////////////////////////////////////////////\n//////////////////////////////////////////////\n// Logging\n// TODO DEPRECATE\n// It's too weird in javascript. \n//////////////////////////////////////////////\n//////////////////////////////////////////////\nvar LogLevel = 1;\n\nfunction SetLogLevel(int) {\n\tLogLevel = int;\n}\n\n// Match https://github.com/rs/zerolog#leveled-logging golang\nvar Log = {\n\tTrace(msg, trace) {\n\t\tlog(msg, 0); // 0\n\t},\n\tDebug(msg, trace) {\n\t\tlog(msg, 1);\n\t},\n\tInfo(msg) {\n\t\tlog(msg, 2);\n\t},\n\tWarn(msg) {\n\t\tlog(msg, 3);\n\t},\n\tError(msg) {\n\t\tlog(msg, 4);\n\t},\n\tFatal(msg) {\n\t\tlog(msg, 5);\n\t},\n\tPanic(msg) {\n\t\tlog(msg, 6);\n\t}\n}\n\n\n/**\n log sets the log level for the application.\n@param {string} msg Message to log.\n@param {number} intLevel Represents the log level.\n@param {boolean} trace If log is a trace.\n */\nfunction log(msg, intLevel, trace) {\n\tif (LogLevel >= intLevel) {\n\t\tif (trace) {\n\t\t\tconsole.trace(msg);\n\t\t} else {\n\t\t\tconsole.debug(msg);\n\t\t\t// console.log(msg);\n\t\t\t// console.trace(msg);\n\t\t}\n\t}\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Notifications\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nnotification sets the main notification for the user.\n@param {string} message Message to display to the user\n@param {string} level \"success\" \"danger\"\n */\nfunction Notification(message, level) {\n\tvar mainAlert = document.querySelector('#cyphrme_toast .toast'); //select id of toast\n\t// Clear previous style. \n\tmainAlert.classList.remove(\"bg-danger\", \"text-white\", \"bg-success\");\n\n\tif (level == \"error\") {\n\t\tmainAlert.classList.add(\"bg-danger\", \"text-white\")\n\t}\n\tif (level == \"success\") {\n\t\tmainAlert.classList.add(\"bg-success\", \"text-white\")\n\t}\n\n\tmainAlert.querySelector(\".toast-body\").innerHTML = message;\n\tvar bsAlert = new bootstrap.Toast(mainAlert); // initialize it\n\tbsAlert.show(); //show it\n\treturn true;\n};\n\n/**\nTODO consider rename to Throw, add new function Error. Error should just be:\nfunction Error(error) {\n\tconsole.error(error)\n\tNotification(msg, \"error\")\n}\n\nfunction Throw(error) {\n\tError(error)\n\tthrow error\n}\n\nError shows an error message for the user and stops function execution via\nthrow. Error should be used at a high level with consumer readable errors.\n\nRecommended not to catch error thrown by Error so that the stack trace is\nlogged. This also stops the calling function's execution, just like a\n\"return\", via `throw`. \n\nUsage:\nInstead of \n\n console.error(e);\n Cyphrme.Notification(e, \"error\")\n return;\n\nJust call Error()\n\n Cyphrme.Error(error);\n\nIf \"error\" is not consumer readable, pass in \"msg\", a consumer readable error\nmessage. Error \"error) itself is still be logged via throw. Example:\n`Cyphrme.Error(e, \"JSON is invalid.\")`\n@param {error} error Javascript error.\n@param {string} [msg] Optional Message.\n@throws {error} Always throws an error.\n */\nfunction Error(error, msg) {\n\tif (isEmpty(msg)) {\n\t\tmsg = error\n\t\tconsole.error(\"Error: \", error)\n\t} else {\n\t\tconsole.error(\"Error: \", error, \"Msg: \", msg)\n\t}\n\tNotification(msg, \"error\")\n\tthrow error\n}\n\n\n/**\nUTKtoHR takes a UTK (UAD, Timestamp, Key) in uppercase hex form, and\nreturn a human readable timestamp format.\n@param {B64} utk\n@returns {Date}\n */\nasync function UTKtoHR(utk) {\n\t// Hacky: Since the way UTK is generated in GO is by slicing up the\n\t// interpreted bytes, we must first convert this to Hex, to properly interpret\n\t// and slice up in Javascript.\n\tlet h = Lib.B64ToHex(utk);\n\tlet trim = h.substring(4);\n\ttrim = trim.substring(0, (trim.length - 4))\n\treturn await UnixToHR(parseInt(trim, 16))\n}\n\n\n/**\nUnixToHR takes a unix timestamp (In seconds. Passing milliseconds returns\nunexpected results), and return a human readable date format.\n@param {number} timestamp Unix timestamp.\n@returns {Date}\n */\nasync function UnixToHR(timestamp) {\n\tlet options = {\n\t\tyear: 'numeric',\n\t\tmonth: 'long',\n\t\tday: 'numeric',\n\t\thour: 'numeric',\n\t\tminute: 'numeric'\n\t}\n\treturn new Date(timestamp * 1000).toLocaleString(\"en-US\", options)\n}\n\n/**\nIsCyphrmeDigest is a sanitization function that checks if string is digest\nlength recognized by Cyphr.me. Non-entropic/non-digests can pass this tests.\n\nTODO this is incomplete, should check canonical b64ut encoding. Include Hex\nand check each length for the correct encoding characters.\n@param {string} string Thing you are checking if it is a hash\n@returns {boolean} True if the thing passed in is a hash.\n@throws {error} Throws error on empty thing passed in.\n*/\nfunction IsCyphrmeDigest(string) {\n\tif (typeof string === \"string\") {\n\t\tswitch (string.length) {\n\t\t\t// b64ut\n\t\t\tcase 43:\n\t\t\tcase 64:\n\t\t\tcase 86:\n\t\t\t\t// BASE37\n\t\t\tcase 50:\n\t\t\tcase 74:\n\t\t\tcase 99:\n\t\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n};\n\n/**\nParseSerializedCozeKey returns a parsd object from the given serialized string.\n\nSerialized forms:\n\nalg:izd:d:x:tmb\nalg:d:x:tmb\nalg:d:x:\nalg:d::\nalg:x:tmb\nalg:tmb\nalg:tmb~chk\nalg:d::tmb\n\nTODO move this to Coze JS Standard.\n@param {UAD} uad\n@returns {CozeKey}\n*/\nfunction ParseSerializedCozeKey(string) {\n\t// console.debug(string)\n\tlet splits = string.split(\":\")\n\tvar CozeKey = {}\n\n\tswitch (splits.length) {\n\t\tdefault:\n\t\t\treturn null;\n\t\tcase 2: // alg:tmb\n\t\t\tCozeKey = {\n\t\t\t\talg: splits[0],\n\t\t\t\ttmb: splits[1],\n\t\t\t}\n\t\t\tbreak\n\t\tcase 3: // alg:x:tmb\n\t\t\tCozeKey = {\n\t\t\t\talg: splits[0],\n\t\t\t\tx: splits[1],\n\t\t\t\ttmb: splits[2],\n\t\t\t}\n\t\t\tbreak\n\t\tcase 4: // alg:d:x:tmb\n\t\t\tCozeKey = {\n\t\t\t\talg: splits[0],\n\t\t\t\td: splits[1],\n\t\t\t\tx: splits[2],\n\t\t\t\ttmb: splits[3],\n\t\t\t}\n\t\t\tbreak\n\t\tcase 5: // alg:izd:d:x:tmb\n\t\t\tCozeKey = {\n\t\t\t\talg: splits[0],\n\t\t\t\tizd: splits[1],\n\t\t\t\td: splits[2],\n\t\t\t\tx: splits[3],\n\t\t\t\ttmb: splits[4],\n\t\t\t}\n\t\t\tbreak\n\t}\n\n\treturn CozeKey\n}\n\n\n/**\nIsHex accepts an id/string and returns true if the string is of one of the\nexpected Base16/Uppercase Hex lengths.\n@param {string} id ID/string being checked.\n@returns {void}\n */\nfunction IsHex(id) {\n\tswitch (id.length) {\n\t\tcase 64:\n\t\tcase 96:\n\t\tcase 128:\n\t\t\treturn true;\n\t}\n\treturn false;\n};\n\n\n/**\nIsBASE37 accepts an id/string and return true if the string is of one of the\nexpected BASE37 lengths.\n@param {string} id ID/string being checked.\n@returns {void}\n */\nfunction IsBASE37(id) {\n\tswitch (id.length) {\n\t\tcase 50:\n\t\tcase 74:\n\t\tcase 99:\n\t\t\treturn true;\n\t}\n\treturn false;\n};\n\n\n/**\nJSONPretty checks for `.jsonPretty` in the DOM classlist for the page, and\nprettifies the text contents of the div.\n@returns {void}\n*/\nasync function JSONPretty() {\n\tlet divs = document.querySelectorAll('.jsonPretty')\n\tdivs.forEach(function (item) {\n\t\tconsole.log(\"JSONPretty\", item)\n\t\tif (item.textContent == \"\") {\n\t\t\treturn\n\t\t}\n\t\tlet json = JSON.parse(item.textContent)\n\t\titem.textContent = JSON.stringify(json, null, 1)\n\t})\n}\n\n\n/**\nHelper function to set URL anchor and scroll to hash anchor. Overwrites existing\nanchors and fragment queries if input is set.\n\nGiven hash must be prepended with \"#\".\n@param {string} [hash] The ID of element to hash link.\n@returns {void}\n*/\nasync function JumpToAnchor(hash) {\n\tlet urlHash = window.location.hash\n\t//console.log(\"jumpToAnchor, \", hash, urlHash)\n\t// If current and given are empty, do nothing. \n\tif (isEmpty(urlHash) && isEmpty(hash)) {\n\t\treturn\n\t}\n\n\tif (isEmpty(hash)) {\n\t\thash = urlHash\n\t}\n\n\t// Get everything before fragment query.\n\t// An anchor is after # and optionally before the next ?. \n\tlet p = hash.split('?')\n\tlet anchor = p[0].substring(1) // past the \"#\" symbol\n\n\tif (isEmpty(anchor)) {\n\t\treturn\n\t}\n\n\tlet element = document.getElementById(anchor)\n\tif (element != null) {\n\t\tconsole.log(\"Jumping to: \" + anchor)\n\t\telement.scrollIntoView()\n\t} else {\n\t\t//// Debugging\n\t\t// console.debug(\"jumpToAnchor Element not found: \" + anchor)\n\t\t// return\n\t}\n\telement.classList.add(\"highlight\")\n\n\n\t// fragment query is after anchor and ?.\n\t// Add back to the url if they were set. \n\tif (!isEmpty(p[1])) {\n\t\tanchor = anchor + \"?\" + p[1]\n\t}\n\t// Don't use `window.location.hash = \"#\" + anchor`, as URLForm reloads on\n\t// hashchange. `pushState` does not trigger a reload.\n\tlet url = new URL(window.location.href)\n\turl.hash = \"#\" + anchor\n\twindow.history.pushState({}, '', url)\n}\n\n\n\n/**\nCollapse sets a click event listener on the given toggleElement to either\ncollapse, or expand the given visibleElement. Uses Bootstrap icons:\nbi-dash-square and bi-plus-square (e.g. )\n\nToggleElement may be the icon itself OR a parent of the icon. visibleElement\nmust be the div being toggled.\n@param {string|element} toggleElement Element that triggers the toggle.\n@param {string|element} visibleElement Element being toggled.\n@param {function} [callback] Optional callback to execute in event listener.\n@param {void}\n*/\nfunction Collapse(toggleElement, visibleElement, callback) {\n\t//console.debug(toggleElement, visibleElement, callback)\n\tif (typeof toggleElement == \"string\") {\n\t\tvar toggleElement = document.getElementById(toggleElement)\n\t}\n\tif (typeof visibleElement == \"string\") {\n\t\tvar visibleElement = document.getElementById(visibleElement)\n\t}\n\n\t// Change Icon\n\ttoggleElement.addEventListener('click', () => {\n\t\t// toggleElement may be the icon itself of the parent element of the icon. \n\t\tvar icon = \"\"\n\t\t// toggleElement is the icon itself.\n\t\tif (toggleElement.classList.contains(\"bi-plus-square\") || toggleElement.classList.contains(\"bi-dash-square\")) {\n\t\t\ticon = toggleElement\n\t\t} else {\n\t\t\t// Assume toggleElement is the parent of the icon. \n\t\t\ticon = toggleElement.querySelector(\".bi-plus-square\")\n\t\t\tif (icon === null) {\n\t\t\t\ticon = toggleElement.querySelector(\".bi-dash-square\")\n\t\t\t}\n\t\t}\n\n\t\tif (ToggleVisible(visibleElement)) {\n\t\t\ticon.classList.remove(\"bi-dash-square\")\n\t\t\ticon.classList.add(\"bi-plus-square\")\n\t\t} else {\n\t\t\ticon.classList.remove(\"bi-plus-square\")\n\t\t\ticon.classList.add(\"bi-dash-square\")\n\t\t}\n\t\tif (typeof callback === 'function') {\n\t\t\tcallback()\n\t\t}\n\t})\n}", "\"use strict\"\n\nimport * as Cyphrme from './cyphrme.js'\n\nexport {\n\tFetch,\n\tFetchHTML,\n\tFetchPost,\n\tBatcher,\n}\n\n/**\nSuccessJSON holds the Cyphr.me server response when calling any JSON API\nendpoint. Success is determined from `success` and not HTTP status codes.\n\n- success: True when request succeeded for the given endpoint. False if an error\n with the request.\n- obj: Object that holds data from the given endpoint called.\n- msg: Server message. Could be a success or fail message.\n- paginate: Server's paginate object.\n- records: Data from the given endpoint.\n@typedef {object} SuccessJSON\n@property {boolean} success\n@property {JSON} [obj] // only guarantee to be populated on success, but\nmay be populated on failure. \n@property {string} [msg] // Only populated for pagination. \n@property {JSON} [paginate]\n@property {JSON} [records]\n*/\n\n/**\nBatcherCallback is called on a per batch basis. Optionally async.\n@async\n@callback BatcherCallback Called after dragster drop for each file.\n@param {File} file\n*/\n\n\n/**\nFetch is the Cyphr.me JSON handler for getFetch().\n@param {string} url URI for upload.\n@returns {JSON} Promise w/ success JSON.\n@throws {error}\n */\nasync function Fetch(url) {\n\tconsole.log(\"Fetch: \" + url)\n\treturn jsonResErrorHandler(await getFetch(url))\n}\n\n/**\nFetchHTML is the Cyphr.me HTML handler for getFetch().\n\nTODO error check that returned HTML is the correct HTML, and not the\nshruggy page. Implement status codes for only for HTML pages. Error HTML\npages should have a different response code.\n@param {string} url URI for upload.\n@returns {html} Promise w/ JSON server response.\n@throws {error}\n */\nasync function FetchHTML(url) {\n\tconsole.log(\"FetchHTML: \" + url)\n\tlet res = await getFetch(url)\n\t// Cyphr.me always returns 200 on success HTML, but another code if error HTML. \n\tif (!res.ok) { // HTTP 200 is OK.\n\t\tconsole.log(\"FetchHTML error body\", res)\n\t\tCyphrme.Error(\"HTML HTTP request failed\") // throws\n\t}\n\treturn res.text() // Don't use res.body() which returns ReadableStream, not text. \n}\n\n/**\nFetchPost is the JSON handler for postFetch(), which is the actual fetch.\n@param {string} url URI for upload.\n@param {formdata} data FormData for upload.\n@returns {JSON} Promise w/ (parsed) JSON server response.\n@throws {error}\n */\nasync function FetchPost(url, data) {\n\tconsole.log(\"FetchPost: \" + url)\n\treturn jsonResErrorHandler(await postFetch(url, data))\n}\n\n/**\npostFetch returns Promise allowing an await. Keywords: ajax. Throws\nerror on 400, and there appears to be nothing that can be done about that.\nhttps://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch\nhttps://stackoverflow.com/questions/40248231/how-to-handle-http-code-4xx-responses-in-fetch-api\n@param {string} url URI for upload.\n@param {FormData} data Data for upload.\n@returns {Promise} Promise w/ Fetch response.\n */\nasync function postFetch(url, data) {\n\t// console.debug(\"Fetch Obj: \", obj)\n\t// const controller = new AbortController()\n\t// setTimeout(() => controller.abort(), 60000) // 1 min timeout\n\t// For sending credentials in requests to both same-origin and cross-origin\n\t// calls, use: `credentials = \"include\"`\n\t// Don't set `Content-Type`, as the browser sets it, while also including\n\t// the `boundary` parameter. Otherwise, the multipart form is sent in the\n\t// body, and are not parsed by the server.\n\t// headers: {\n\t// \t'Content-Type': 'application/json'\n\t// },\n\treturn fetch(url, {\n\t\tmethod: \"POST\", // *GET, POST\n\t\tmode: 'cors', // no-cors, *cors, same-origin\n\t\tcache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached\n\t\tcredentials: 'same-origin', // include, *same-origin, omit\n\t\tredirect: 'follow', // manual, *follow, error\n\t\treferrerPolicy: 'no-referrer',\n\t\tbody: data,\n\t\t// signal: controller.signal,\n\t})\n}\n\n/**\ngetFetch returns Promise allowing an await. See Verifier's `get()`\nfor an example of on page error handling. Keywords: ajax. Still throws error on\n400, and there appears to be nothing that can be done about that.\nhttps://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch\nhttps://stackoverflow.com/questions/40248231/how-to-handle-http-code-4xx-responses-in-fetch-api\n@param {string} url URI for upload.\n@returns {Response} Promise w/ Fetch response.\n*/\nasync function getFetch(url) {\n\treturn fetch(url, {\n\t\tmethod: \"GET\", // *GET, POST\n\t\tmode: 'cors', // no-cors, *cors, same-origin\n\t\tcache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached\n\t\tcredentials: 'same-origin', // include, *same-origin, omit\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json'\n\t\t},\n\t\tredirect: 'follow', // manual, *follow, error\n\t\treferrerPolicy: 'no-referrer',\n\t})\n}\n\n/**\njsonResErrorHandler is the main JSON error handler for GET and POST requests.\nReturns the parsed JSON response from the server. The response passed in must be\na resolved Fetch Response, and not a promise.\n@param {Response} res Fetch response from the server.\n@returns {JSON}\n@throws {error}\n */\nasync function jsonResErrorHandler(res) {\n\tconsole.debug(res)\n\t// Cyphr.me always returns 200 on JSON api, because doing switching on HTTP\n\t// and JSON is dumb, and handing errors on JSON means that JSON is independent\n\t// of transport (HTTP) which is good. If not 200, something is very wrong.\n\tif (!res.ok) { // HTTP 200 is OK.\n\t\tCyphrme.Error(\"Unable to communicate with server. \" + res.url) // throws\n\t}\n\n\t// Both empty `` and empty `{}` json.join() don't result in error. On empty,\n\t// success checking after won't have parsd.msg, so error is empty which is bad\n\t// user experience. \n\ttry {\n\t\tvar parsd = await res.json()\n\t\tconsole.debug(parsd)\n\t} catch (e) {\n\t\tCyphrme.Error(\"Error parsing JSON response: \" + e) // Throws\n\t}\n\tif (!(Object.keys(parsd).includes('success')) || parsd.success == false) { // Object may not include success or success may be false. \n\t\tif (Object.keys(parsd).includes('msg') && (!isEmpty(parsd.msg))) {\n\t\t\tCyphrme.Error(parsd.msg) // Throws\n\t\t} else {\n\t\t\tCyphrme.Error(\"Server communication error. JSON call failed and response msg is empty: \" + parsd) // Throws\n\t\t}\n\t}\n\treturn parsd\n}\n\n/**\nBatcher uploads the given values in the given batch size. The given callback is\nnot synchronous. Errors are only thrown if `aSink=true`, because `await` is\nneeded for errors to be throwable in an async function.\n\n TODO probably accept an object with params, batcherArgs? // TODO Jared. \n@param {number} batchSize Batch size to split the given values into.\n@param {*[]} values Data being sent to the endpoint.\n@param {string} endpoint Endpoint to send the data.\n@param {BatcherCallback} [callback] Optional callback to be executed asynchronously on successful response.\n@param {boolean} [aSink] (async) Runs asynchronously when true. Can't use `async` as name.\n@returns {void}\n@throws {error}\n */\nasync function Batcher(batchSize, values, endpoint, callback, aSink) {\n\t// console.debug('Running batcher...')\n\n\t// Synchronous fetch uploader for batcher. (Note: The given callback is not\n\t// synchronous.) For asynchronous uploads, do not call this function with an\n\t// await.\n\tasync function f(endpoint, formData, batchSize, callback) { // TODO remove signature if possible\n\t\ttry {\n\t\t\tvar response = await FetchPost(endpoint, formData) // throws\n\t\t\t// console.debug(\"Response: \", response)\n\t\t\tif (isEmpty(response)) {\n\t\t\t\tCyphrme.Error(\"Something went wrong with your request.\")\n\t\t\t}\n\t\t\tif (!response.success) {\n\t\t\t\tconsole.error(\"batcher: \", response.msg)\n\t\t\t\tCyphrme.Error(response.msg)\n\t\t\t}\n\t\t\tif (!isEmpty(callback)) {\n\t\t\t\tcallback(response.obj, batchSize)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthrow error // Pass up\n\t\t}\n\t}\n\n\t// TryCatch cannot be done here, and must be done in `f`. Functions that call\n\t// batcher must wrap in a TryCatch if needing to handle the errors that may be\n\t// thrown by `FetchPost`. `await` is needed for getting the errow thrown from\n\t// the function being called. e.g. `await f` vs `f` only throws an error from\n\t// the call with the await.\n\tfor (let i = batchSize; i < values.length + batchSize; i += batchSize) {\n\t\tlet batch = values.slice(i - batchSize, i)\n\t\tlet formData = new FormData()\n\t\t// formData.append('error', \"true\") // For testing.\n\t\tformData.append('cozes', JSON.stringify(batch))\n\t\tif (aSink) {\n\t\t\tawait f(endpoint, formData, batch.length, callback)\n\t\t\tcontinue\n\t\t}\n\t\tf(endpoint, formData, batch.length, callback) // Use aSink=true for synchronous behavior.\n\t}\n}", "\"use strict\"\n\nimport * as Cyphrme from './cyphrme.js'\n\nexport {\n\tGetCurrentURLParams,\n\tSetPaginationArrows,\n\tInitPagRec,\n\tGenTable,\n\tAddTableRow,\n\tAddTableRowFirst,\n}\n\n/**\n@typedef {import('../../../pkg/cozejs/typedef.js').B64} B64\n@typedef {import('../../../pkg/cozejs/typedef.js').Can} Can\n*/\n\n/**\nPagRec holds the 'Paginate' object and the 'Records' for the given page.\n\n- pag: Paginate object with pagination details form the server.\n- rec: Records array of objects for the current page.\n@typedef {object} PagRec\n@property {Paginate} [pag]\n@property {Records} [rec]\n*/\n\n/**\nRecords is an array of objects. The objects are the subject matter for the\ncurrent page.\n@typedef {object[]} Records\n */\n\n/**\nPaginate is an object that holds the necessary pagination information for\nnavigating from the current page. This should match the server's representation\nof the paginator.\n\n- pivot: The record ID/pivot for the next page.\n- pivot_b: The record ID/pivot for going backwards.\n- limit: The limit for the query. 1. how many records to retrieve, and 2. the\n limit for how many records appear per page.\n- depth: For hierarchical structures, depth is how deep. \n- order: The order in which the query is being applied. \"-\" for descending and\n \"\" for ascending.\n- subject: The subject matter for the page. Only: [\"uad\", \"parent\", \"root\"]\n- subject_value: Value for subject. \n- has_more: The page has more. Must always in in alignment with `pivot`. (Show\n More button)\n- has_prev: The page has previous. Must always in in alignment with `pivot_b`.\n (Show Previous button)\n- record_number: The record number on the page. Must always be in alignment with\n `records` (the record object).\n- page: The page number for what page the user currently is on. Must *not* be\n used in calculations.\n- limits: Supported limits for the limit dropdown.\n- max_limit: Maximum supported limit for the page.\n@typedef {object} Paginate\n@property {B64} [pivot]\n@property {B64} [pivot_b]\n@property {number} [limit]\n@property {number} [depth]\n@property {string} [order]\n@property {string} [subject]\n@property {B64} [subject_value]\n@property {boolean} [has_more]\n@property {boolean} [has_prev]\n@property {number} [record_number]\n@property {number} [page]\n@property {string[]} [limits]\n@property {number} [max_limit]\n */\n\n/**\nURLQueryParameters holds the information from the URL, that is relevant for\nquery and pagination information.\n\n- params: The current search params, represented as a string.\n- page: The page number for the current page.\n- order: The order of the search results.\n- currentURL: The URLSearchParams object for the current URL.\n- leftPR: The left Pivot Record, useful for pagination arrows. E.g. \"gt\"\n- rightPR: The right Pivot Record, useful for pagination arrows.\n- type: The container type. E.g. \"PJ\", \"Page\", \"Item\".\n@typedef {object} URLQueryParameters\n@property {string} params\n@property {number} page\n@property {boolean} order\n@property {URLSearchParams} currentURL\n@property {string} [leftPR] \n@property {string} [rightPR]\n@property {string} [type]\n */\n\n\n/**\nInitPagRec ingests a \"response\" and sets up PagRec for pages that supports\npagination. \n\n1. Sets up GUI navigation.\n2. Inits PagRec derivative variables.\n3. Triggers GUI error on error.\n@param {string|object} res String/Object of JSON.\n@returns {PagRec}\n@throws {error}\n */\nasync function InitPagRec(res) {\n\ttry {\n\t\tif (isEmpty(res)) {\n\t\t\tthrow new Error('No records found.')\n\t\t}\n\t\tif (typeof res !== 'object') {\n\t\t\tres = JSON.parse(res)\n\t\t}\n\t\t///////////////////\n\t\t// Pagination controls and page number display.\n\t\tif (isEmpty(res.pag)) {\n\t\t\tconsole.debug('res.pag is empty')\n\t\t\treturn\n\t\t}\n\t\tif (isEmpty(res.pag.page)) {\n\t\t\tres.pag.page = 1\n\t\t}\n\t\tdocument.querySelectorAll('.pageNumber').forEach(function (item) {\n\t\t\titem.textContent = res.pag.page\n\t\t})\n\t\tsetPaginateDropdown(res.pag) // Sets all Sort Order and Limit dropdowns.\n\t\tSetPaginationArrows(res) // Sets all forward/backward buttons.\n\n\t\t// Set individual record numbers. Calculated based on limit and page. \n\t\tres.pag.page_start_record_number = (res.pag.limit * (res.pag.page - 1)) + 1\n\n\t\treturn res\n\t} catch (e) {\n\t\tconsole.error(e)\n\t\tCyphrme.Error('No records found')\n\t}\n}\n\n\n/**\nsetPaginateDropdown highlights the current limit on the page.\n@param {number|string} paginate Number or String. Page limit i.e 10\n@returns {void}\n */\nfunction setPaginateDropdown(paginate) {\n\t// console.debug(\"setPaginateDropdown:\", paginate)\n\n\t// Manually create limit dropdown options when `limits` is given in paginate.\n\tif (!isEmpty(paginate.limits)) {\n\t\tdocument.querySelectorAll('.paginationDropdownLimit').forEach(function (div) {\n\t\t\tdiv.innerHTML = \"\" // Clear out default options.\n\t\t\tfor (let limit of paginate.limits) {\n\t\t\t\tlet a = document.createElement('a')\n\t\t\t\ta.classList.add('dropdown-item')\n\t\t\t\ta.dataset.value = limit\n\t\t\t\ta.textContent = limit\n\t\t\t\tdiv.append(a)\n\t\t\t}\n\t\t})\n\t}\n\n\tdocument.querySelectorAll('.urlFormDropdownLink a').forEach(function (a) {\n\t\tlet pagCopy = {\n\t\t\t...paginate\n\t\t}\n\t\t// console.debug(\"A's:\", a)\n\t\tlet urlParam = a.parentElement.dataset.urlformparam\n\t\t// console.debug(\"urlParam:\", urlParam)\n\n\t\t// Add active if current value is the value\n\t\tif (pagCopy[urlParam] == stringToType(a.dataset.value)) {\n\t\t\t// console.debug(\"active\")\n\t\t\ta.classList.add('active')\n\t\t}\n\t\tlet canon = [\"limit\", \"order\", \"piv_comop\", \"depth\", \"type\"]\n\t\tpagCopy[urlParam] = stringToType(a.dataset.value)\n\t\ta.href = '?' + GenURLFromPaginate(pagCopy, canon) + '&page=1'\n\t})\n}\n\n/**\nstringToType parses a string, into a boolean, integer, or a string.\n@param {string} s The URL Form param's value.\n@returns {boolean|number|string}\n */\nfunction stringToType(s) {\n\tif (s.toLowerCase() === 'true') {\n\t\treturn true\n\t}\n\tif (s.toLowerCase() === 'false') {\n\t\treturn false\n\t}\n\tlet i = parseInt(s, 10)\n\tif (Number.isInteger(i)) {\n\t\treturn i\n\t}\n\treturn s\n}\n\n/**\nAccepts a pagination object, that populates the pagination arrows on the page.\nReturns a copy of the records, after possible modifications have been made.\nThrows error when no objects are passed in.\n@param {PagRec} pagrec\n@returns {PagRec}\n@throws {error}\n*/\nasync function SetPaginationArrows(pagrec) {\n\t// console.log(\"SetPaginationArrows:\", pagrec)\n\tif (isEmpty(pagrec)) {\n\t\tthrow new Error(\"empty pagrec passed in\")\n\t}\n\tlet paginate = pagrec.pag\n\tlet copy = {\n\t\t...pagrec.rec\n\t}\n\tlet urlp = GetCurrentURLParams()\n\t// console.debug(\"url Params:\", urlp)\n\t// \"Show less arrow/Backward button\"\n\tif (!isEmpty(paginate.pivot_b)) {\n\t\tdocument.querySelectorAll('.showLessBtn').forEach(function (item) {\n\t\t\tShow(item)\n\t\t\titem.href = (item, Cyphrme.Site + window.location.pathname + (\"?\" + urlp.params + \"&pivot=-\" + paginate.pivot_b + ('&piv_comop=' + urlp.leftPF) + ('&order=' + urlp.order)))\n\t\t\tif (!isEmpty(urlp.type)) {\n\t\t\t\titem.href += '&type=' + paginate.type\n\t\t\t}\n\t\t})\n\t}\n\t// \"Show more arrow/Forward button\"\n\tif (!isEmpty(paginate.pivot)) {\n\t\tdocument.querySelectorAll('.showMoreBtn').forEach(function (item) {\n\t\t\tShow(item)\n\t\t\titem.href = (item, Cyphrme.Site + window.location.pathname + (\"?\" + urlp.params + \"&pivot=\" + paginate.pivot + ('&piv_comop=' + urlp.rightPF) + ('&order=' + urlp.order) + ('&page=' + (paginate.page + 1))))\n\t\t\tif (!isEmpty(urlp.type)) {\n\t\t\t\titem.href += '&type=' + paginate.type\n\t\t\t}\n\t\t})\n\t}\n\t// console.debug(copy)\n\treturn copy\n}\n\n/**\ngetCurrentURLParams returns an object, populated with the current URL parameters\nsupported.\n\nExample of current support:\n{\n\"params\":\"&limit=5&depth=2\",\n\"leftPR\":\"gt\",\n\"rightPR\":\"lteq\",\n\"page\":1,\n\"order\":true,\n\"currentURL\":\"pivot=LRdOw3jOBNGSxb2QqWOWT8XyZUi5EoXmNvZff1Yt-UQ&piv_comop=lteq&order=false&page=3\"\n}\n@returns {URLQueryParameters}\n */\nfunction GetCurrentURLParams() {\n\tlet params = \"\"\n\tlet leftPF = \"gt\"\n\tlet rightPF = \"lteq\"\n\tlet page = 1\n\tlet ord = false\n\tlet order = false\n\tlet type = \"\"\n\tlet current = URLSearchParams\n\t// console.debug(paginate)\n\t// Continue Limit & Depth Params, if they exist.\n\tif (!isEmpty(window.location.search)) {\n\t\tcurrent = new URLSearchParams(window.location.search)\n\t\tlet limit = current.get(\"limit\")\n\t\tlet depth = current.get(\"depth\")\n\t\t// console.debug(\"url:\", url.toString())\n\t\t// console.debug(\"limit:\",limit)\n\t\t// console.debug(\"depth:\",depth)\n\t\t// console.debug(\"order:\",order)\n\t\tif (!isEmpty(limit)) {\n\t\t\tparams += '&limit=' + limit\n\t\t}\n\t\tif (!isEmpty(depth)) {\n\t\t\tparams += '&depth=' + depth\n\t\t}\n\t\tif (!isEmpty(current.get(\"page\"))) {\n\t\t\tpage = parseInt(current.get(\"page\"), 10)\n\t\t}\n\t\tif (!isEmpty(current.get(\"order\"))) {\n\t\t\torder = current.get(\"order\")\n\t\t}\n\t\tif (order === true || order == 'true') {\n\t\t\tleftPF = \"lt\"\n\t\t\trightPF = \"gteq\"\n\t\t\tord = true\n\t\t}\n\t\tif (!isEmpty(current.get(\"type\"))) {\n\t\t\ttype = current.get(\"type\")\n\t\t}\n\t}\n\t// console.debug(leftPF)\n\treturn {\n\t\t\"params\": params,\n\t\t\"leftPF\": leftPF,\n\t\t\"rightPF\": rightPF,\n\t\t\"page\": page,\n\t\t\"order\": ord,\n\t\t\"type\": type,\n\t\t\"currentURL\": current\n\t}\n}\n\n\n/**\nGenURLFromPaginate generates a URL Parameter search, meant for pagination\nqueries. If canon is not set, the URL is generated from the entirety of the\nobject.\n@param {Paginate} paginate\n@param {Can} [canon] Optional canon fields to generate the URL.\n */\nfunction GenURLFromPaginate(paginate, canon) {\n\tlet keys = canon\n\tif (isEmpty(canon)) {\n\t\tkeys = Object.keys(paginate)\n\t}\n\tlet urlParams = \"\"\n\tfor (let i = 0; i < keys.length; i++) {\n\t\t// support URL flags (e.g. ?order)\n\t\tif (isEmpty(keys[i])) {\n\t\t\tcontinue\n\t\t}\n\t\turlParams += '&' + keys[i] // add as flag\n\t\tif (!isEmpty(paginate[keys[i]])) {\n\t\t\turlParams += '=' + paginate[keys[i]] // if value is set, set value on flag\n\t\t}\n\t}\n\treturn urlParams\n}\n\n\n/**\nGenTable creates headers and returns the table html element.\n@param {string[]} headers Header column names.\n@returns {HTMLElement}\n */\nfunction GenTable(headers) {\n\theaders.unshift('') // Always add first col for checkbox\n\tlet t = document.querySelector(\".gen_js_table\")\n\tlet th = t.querySelector(\"thead tr\")\n\tfor (var i = 0; i < headers.length; i++) {\n\t\tlet h = document.createElement(\"th\")\n\t\th.textContent = headers[i]\n\t\tth.append(h)\n\t}\n\treturn t\n}\n\n\n/**\nAddTableRow inserts a row into the given table from the parameter `row`.\n@param {HTMLElement} TableElement.\n@param {HTMLElement[]} row\n@param {B64} [id] optional id for setting id on table rows\n@returns {void}\n */\nfunction AddTableRow(TableElement, row, id) {\n\tTableElement.querySelector(\"tbody\").append(createTableRow(row, id))\n}\n\n/**\nAddTableRowFirst inserts a record at the beginning of the table.\n@param {HTMLElement} TableElement.\n@param {HTMLElement[]} row\n@param {B64} [id] optional id for setting id on table rows\n@returns {void}\n */\nfunction AddTableRowFirst(TableElement, row, id) {\n\tTableElement.querySelector(\"tbody\").prepend(createTableRow(row, id))\n}\n\n/**\ncreateTableRow populates a table row, with the given row data. The row is\nreturned populated, without being added to the table. Returns table row with\npopulated data.\n@param {HTMLElement[]} row\n@param {B64} [id] optional id for setting id on table\nrows\n@returns {HTMLTableRowElement}\n */\nfunction createTableRow(row, id) {\n\tlet tr = document.createElement(\"tr\")\n\tif (!isEmpty(id)) {\n\t\ttr.id = id\n\t}\n\t// Always adds checkbox to first column of the table\n\trow.unshift(document.querySelector('.table_checkbox').cloneNode(true))\n\tfor (var i = 0; i < row.length; i++) {\n\t\tlet td = document.createElement(\"td\")\n\t\tif (row[i] instanceof HTMLElement) {\n\t\t\ttd.append(row[i])\n\t\t} else {\n\t\t\ttd.innerHTML = row[i]\n\t\t}\n\t\ttr.append(td)\n\t}\n\treturn tr\n}", "\"use strict\"\n\n// HTML elements that this file uses:\n// .cyphrStars\n// #reviewForm\n// #commentSection\n\nimport * as Login from './login.js'\nimport * as Cyphrme from './cyphrme.js'\nimport * as CVA from './cva.js'\nimport * as Ajax from './ajax.js'\nimport * as Pag from './paginate.js'\nimport * as Lib from '../lib/lib~fv=LNbMt1n5.min.js'\nimport '../pkg/urlform~fv=VhWyOSvq.min.js' // Namespaced as 'URLForm'.\n\n/**\n@typedef {import('../../../pkg/cozejs/typedef.js').B64} B64\n@typedef {import('../../../pkg/cozejs/typedef.js').Pay} Pay\n@typedef {import('./paginate.js').Paginate} Paginate\n@typedef {import('./paginate.js').PagRec} PagRec\n */\n\n/**\nComment holds fields for a comment/review.\n\n- id: The B64 ID of the comment (from the coze).\n- uad: The B64 ID of the user that owns/created the comment.\n- text: The text contents of the comment.\n- time: The time the comment was first seen by Cyphr.me.\n- parent: The parent that the comment is attached to.\n- root: The page the comment was originally made from.\n- child_ac: The ID of the model's child.\n- edit: Whether or not the comment is an edit or a reply.\n- modelID: The Model ID, if a comment is attached to a model page.\n- displayName: The comment owner's display name.\n- images: Any images attached to the comment by the comment owner. // TODO\n- rating: The number of rating \"Points\" as given by the user.\n- ratingDenom: The Rating's denominator. The scale of the rating. (e.g 5 for 1/5\n or 5/5).\n- counterfeit: Whether or not the comment is serving as a counterfeit report.\n- purchased: Whether or not the user that scanned the item purchased the item.\n- purhcaseLocation: The location that the product was purchased from as reported\n by the user.\n- children: Any children comments (replies) on this comment.\n@typedef {object} Comment \n@property {B64} id\n@property {B64} uad\n@property {string} text\n@property {number} time\n@property {B64} root\n@property {B64} [parent]\n@property {B64} [child_ac]\n@property {boolean} [edit]\n@property {B64} [modelID]\n@property {string} [displayName]\n@property {B64[]} [images] - TODO\n@property {number} [rating]\n@property {number} [ratingDenom]\n@property {boolean} [counterfeit]\n@property {boolean} [purchased]\n@property {string} [purchaseLocation]\n@property {Comment[]} [children]\n */\n\n/**\nCommentPay are the signable fields on a Comment/Review. Standard Coze fields\nshould be prepended to a CommentPay to be a valid Coze.\n\nRequired fields:\n- root: The root subject that the comment is attached to.\n\nFields marked with ** mean that if present, the comment is considered to be a\nreview.\n\nOptional fields:\n- id: ID of the comment, when being updated/edited.\n- text: The text area value.\n- parent: The ID that the comment is directly attached to. e.g. another\n comment's ID.\n- child_ac: The ID of a model's child.\n- counterfeit_report: True if user reporting the current page as a counterfeit.\n- rating: The user's rating out of 5 for the given page.\n- rating_denom: Rating denom (5 for 0/5). Uses default page denom if rating is\n given.\n- purchase_location: The purchase location for where a use may have purchased\n the item for the given page.\n- purchased: Whether or not the user on the page has purchased the product that\n the page is for.\n- edit: True if a user is editing a given comment/review. (Set by application)\n- rtd: (reply to digest) The digest of the text of the comment being replied to.\n@typedef {object} CommentPay\n@property {B64} root\n@property {B64} [id]\n@property {B64} [parent]\n@property {B64} [child_ac]\n@property {string} [text]\n@property {boolean} [counterfeit_report]\n@property {number} [rating]\n@property {number} [rating_denom]\n@property {string} [purchase_location]\n@property {boolean} [purchased]\n@property {boolean} [edit]\n@property {boolean} [rtd]\n */\n\n/**\nEditableReview holds review fields for updating a review. If any of the\noptional fields are present, then `is_review` is set to true.\n\nis_review: Whether the comment has any optional review fields.\n\nelement: The HTML element for the comment/review.\n\nAll other optional fields are the same as `CommentPay`.\n@typedef {object} EditableReview\n@property {boolean} is_review\n@property {Element} element\n@property {number} [rating]\n@property {boolean} [purchased]\n@property {string} [purchase_location]\n@property {boolean} [counterfeit]\n */\n\nexport {\n\tCreateReviews,\n\tCommentReviewModule,\n\tCreateSubmitReviewForm,\n\tSetPageRatingStar,\n\tSubmitReview,\n\tSubmitComment,\n\tToggleReviewForm,\n}\n\n// global for callbacks\n/** @type {PagRec} */\nvar PR = {}\n\n// URLFormJS form options\nvar initedFormOptions\n\n/**\nCommentReviewModule inits the ReviewComment module.\nCreates the Comments, and displays them on the page.\n@param {PagRec} pr\n@returns {void}\n */\nasync function CommentReviewModule(pr) {\n\t// console.log(\"CommentReviewModule:\", pr)\n\n\tPR = pr // set global\n\tlet cmnt = document.getElementById('CommentSection')\n\tcmnt.append(CreateReviews(PR.rec))\n\tif (isEmpty(PR.rec)) {\n\t\tPR.rec = []\n\t}\n\t// Show submitted review if logged in and not submitted review. // TODO fix show when review is on page 2. \n\tif (!isEmpty(Login.UAD)) {\n\t\tlet subed = false\n\t\tfor (let rec of PR.rec) {\n\t\t\tif (rec.uad == Login.UAD) {\n\t\t\t\tsubed = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif (!subed) {\n\t\t\tShow(document.getElementById(\"reviewFormDiv\"))\n\t\t}\n\t}\n\n\t// \"Show more comments\" button on Comment Threads\n\tif (PR.pag.has_more) {\n\t\tlet btn = document.querySelector('#showMoreCommentsBtn')\n\t\tif (btn != null) {\n\t\t\tbtn.href = Cyphrme.Page.Comments + \"/\" + PR.pag.pivot + \"?page=2\"\n\t\t\tShow(btn)\n\t\t}\n\t}\n\n\tif (PR.pag.lock_comments) {\n\t\tShow(\"lockCommentsOnPage\")\n\t\tDisable(\"submitReviewBtn\")\n\t}\n\n\t// Init URLFormJS.\n\tinitedFormOptions = URLForm.Init(URLForm.DefaultFormOptions)\n\n\tShow(cmnt)\n}\n\n\n/**\nCreateReviews creates all reviews and comments for a page and returns and\nHTML div, with formatting. Review comment children are included recursively.\nFor each comment, the process goes: Review -> Comment -> Comment Children,\nand then returns such tree in divs, ready to append to the main thread.\n@param {Comment[]} revs Review/Comments, with comment children.\n@returns {Element}\n */\nfunction CreateReviews(revs) {\n\tif (isEmpty(revs)) {\n\t\tlet div = document.getElementById('noCommentsOnPage')\n\t\tShow(div)\n\t\treturn div\n\t}\n\t// Range through each 'top-level' comment, to create the individual comment's\n\t// thread. If the comment is a review, the review fields are populated.\n\tlet div = document.createElement('div')\n\tfor (let rev of revs) {\n\t\tdiv.append(createReview(rev))\n\t}\n\treturn div\n}\n\n\n/**\nappendChild accepts a div, and a slice of children, to then create a comment for\neach and then return the div. Also handles pagination for comment depth, and\ncreate a show more link on the comment once it reaches the specified depth.\n\nHelper function to avoid recursive calls on createComment.\n@param {Element} div Div of the thing to append the children to.\n@param {Comment} comment Comment/review including any children.\n@returns {Element}\n */\nfunction appendChildren(div, comment) {\n\t// console.debug(div)\n\tlet depth = comment.children.length\n\tif (comment.has_more_children) {\n\t\tdepth = comment.children.length - 1\n\t\tlet link = div.querySelector(\".showMoreCommentsLink\")\n\t\tlet current = Pag.GetCurrentURLParams()\n\t\tcurrent.params += \"&page=\" + (current.page + 1)\n\t\tif (!isEmpty(current.limit)) {\n\t\t\tcurrent.params += \"&limit=\" + current.limit\n\t\t}\n\t\tif (current.order) {\n\t\t\tcurrent.params += \"&order=true&piv_comop=gteq\"\n\t\t}\n\t\t// console.debug('urlParams:', current.params)\n\t\t// console.debug('current:', current)\n\t\tlink.href = Cyphrme.Page.Comments + \"/\" + comment.children[depth].id + \"?\" + current.params\n\t\tShow(link)\n\t}\n\tfor (var i = 0; i < depth; i++) {\n\t\tlet child = createComment(comment.children[i])\n\t\tdiv.querySelector(\".children\").append(child)\n\t}\n\treturn div\n}\n\n/**\ncreateReview returns a populated review card with children. \n@param {Comment} comment\n@returns {Element}\n */\nfunction createReview(comment) {\n\tlet card = createComment(comment)\n\tlet ce = card.querySelector('.comment_extra')\n\tcreateExtraAppend(ce, \"Rating\", comment.rating)\n\tcreateExtraAppend(ce, \"Purchased\", isBool(comment.purchased))\n\tcreateExtraAppend(ce, \"Purchase Location\", comment.purchase_location)\n\tcreateExtraAppend(ce, \"Counterfeit\", comment.counterfeit_report)\n\treturn card\n}\n\n/**\ncreateExtraAppend generates a span element with a Key:Value pair.\nIf empty,nothing happens.\n@param {Element} Element HTMLElement being appended to.\n@param {string} Name Class name with removed whitespace for adding to span.\n@param {string} Value Value of the span\n@returns {void}\n */\nfunction createExtraAppend(Element, Name, Value) {\n\tif (isEmpty(Value)) {\n\t\treturn\n\t}\n\tlet s = document.createElement('span')\n\tlet c = Name.replace(/\\s/g, '') // remove whitespace from name\n\ts.classList.add(c)\n\ts.innerText = \" | \" + Name + \": \" + Value\n\ts.dataset.value = Value // Used for editable reviews/comments.\n\tElement.append(s)\n}\n\n/**\ncreateComment returns a populated comment card.\n@param {Comment} comment\n@returns {Element}\n */\nfunction createComment(comment) {\n\tlet template = document.getElementById('commentTemplate')\n\tlet commentDiv = template.content.cloneNode(true)\n\tlet card = commentDiv.querySelector('.card')\n\n\tif (comment.deleted) {\n\t\tcard.querySelector('.text-muted').remove()\n\t\tcard.querySelector('.comment').textContent = \" Comment has been deleted\"\n\t\tcard.querySelector('.buttons').remove()\n\t\tcard.classList.add('border-warning', 'text-start')\n\t\tif (!isEmpty(comment.children)) {\n\t\t\tcard = appendChildren(card, comment)\n\t\t}\n\t\treturn card\n\t}\n\n\tlet replyBtn = card.querySelector('.commentReplyBtn')\n\tlet editBtn = card.querySelector('.commentEditBtn')\n\tlet deleteBtn = card.querySelector('.commentDeleteBtn')\n\n\tcard.setAttribute('data-commentID', comment.id)\n\tcard.setAttribute('id', comment.id)\n\n\tlet options = {\n\t\tyear: 'numeric',\n\t\tmonth: 'long',\n\t\tday: 'numeric',\n\t\thour: 'numeric',\n\t\tminute: 'numeric'\n\t}\n\tlet createdDate = new Date(comment.time * 1000).toLocaleString(\"en-US\", options)\n\tlet updatedDate = new Date(comment.updated * 1000).toLocaleString(\"en-US\", options)\n\tif (isEmpty(comment.updated) || comment.updated == 0) {\n\t\tupdatedDate = 'Never'\n\t\tHide(card.querySelector('.updated').parentElement)\n\t}\n\tlet ownerLink = \"\"\n\tif (!isEmpty(comment.display_name)) {\n\t\townerLink = comment.display_name\n\t} else {\n\t\townerLink = comment.uad\n\t}\n\tif (ownerLink.length > 10) {\n\t\townerLink = ownerLink.substring(0, 10) + \"...\"\n\t}\n\tlet text = comment.text\n\tif (isEmpty(text)) {\n\t\ttext = '[no text]'\n\t}\n\tcard.querySelector('.comment').innerText = text\n\tcard.querySelector('.owner').innerText = ownerLink\n\tcard.querySelector('.owner').href = '/user/id/' + comment.uad\n\tcard.querySelector('.created').innerText = createdDate\n\tcard.querySelector('.updated').innerText = updatedDate\n\tcard.querySelector('.commentRaw').innerText = JSON.stringify(comment, null, \" \")\n\tcard.querySelector('.commentLink').setAttribute('href', Cyphrme.Page.Comments + \"/\" + comment.id)\n\n\tCyphrme.Collapse(card.querySelector(\".bi-dash-square\"), card.querySelector('.card-hide'))\n\n\t// Hide Reply, if not logged in.\n\tif (isEmpty(Login.UAD)) {\n\t\tHide(replyBtn)\n\t} else {\n\t\treplyBtn.addEventListener('click', function () {\n\t\t\tcommentReplyOrEdit(card, false)\n\t\t})\n\t}\n\tif (PR.pag.lock_comments) {\n\t\tDisable(replyBtn)\n\t}\n\t// Show buttons if owner.\n\tif (Login.UAD == comment.uad) {\n\t\teditBtn.addEventListener('click', function () {\n\t\t\tcommentReplyOrEdit(card, true)\n\t\t})\n\t\tdeleteBtn.addEventListener('click', function () {\n\t\t\tcommentDelete(card)\n\t\t})\n\t\tShow(editBtn)\n\t\tShow(deleteBtn)\n\t}\n\n\t// Create and append children, if they exist.\n\tif (!isEmpty(comment.children)) {\n\t\tcard = appendChildren(card, comment)\n\t}\n\treturn card\n}\n\n/**\ncommentReplyOrEdit creates a new reply div on the page or toggles existing ones.\nAlso used for editing comments. Reply div is inside parent.\n@param {Element} div Comment div element on page.\n@param {bool} [edit] True if the comment is being edited, and is not a new reply.\n@returns {void}\n */\nfunction commentReplyOrEdit(div, edit) {\n\t// console.log(div, edit)\n\tvar rd = div.querySelector(\".replyDiv\")\n\n\t// Check if a reply exists. If so, toggle visibility.\n\tif (rd !== null) {\n\t\tlet change = setReplyFunc(div, edit)\n\n\t\tif (!change || (change && !isVisible(rd))) {\n\t\t\tToggleVisible(rd)\n\t\t}\n\t\treturn\n\t}\n\n\t// Container Div for whole reply\n\tlet templ = document.querySelector('#commentReplyDivTempl').content.cloneNode(true)\n\t// console.log(\"Templ\", templ)\n\tlet replyDiv = templ.querySelector('div')\n\t// console.log(\"Div\", replyDiv)\n\n\t// TODO typedef\n\tvar reviewObj = editableReview(div, replyDiv)\n\tconsole.debug(reviewObj)\n\n\t// For event listeners on comment submission.\n\tlet submitFunc = async function (event) {\n\t\tevent.preventDefault()\n\t\tlet comment = {}\n\t\tcomment.text = div.querySelector(\"textarea\").value\n\t\tcomment.id = div.getAttribute(\"data-commentid\")\n\t\tcomment.edit = div.classList.contains('edit')\n\t\tcomment.root = PR.pag.subject_value\n\n\t\t// If comment is a review, add populated fields to pay.\n\t\tif (reviewObj.is_review) {\n\t\t\tcomment = await setReviewPay(reviewObj.element, comment)\n\t\t}\n\n\t\tif (!comment.edit) {\n\t\t\t// Not edit means it is a reply.\n\t\t\tcomment.parent = comment.id\n\t\t}\n\t\t// console.debug(comment)\n\t\tSubmitComment(comment)\n\t}\n\n\treplyDiv.querySelector('button').addEventListener('click', submitFunc)\n\n\tif (!reviewObj.is_review) {\n\t\tlet textArea = replyDiv.querySelector('textarea')\n\t\ttextArea.addEventListener('keydown', function (event) {\n\t\t\tif (event.code == \"Enter\" && event.shiftKey) {\n\t\t\t\tsubmitFunc(event)\n\t\t\t}\n\t\t})\n\t\tShow(textArea)\n\t}\n\tdiv.querySelector(\"div.row\").append(replyDiv)\n\tsetReplyFunc(div, edit) // Set function for reply div.\n}\n\n/**\neditableReview checks if review fields are populated on the given comment\ndiv element, and if so, assumes the comment is a review. The review form\nis populated in the comment's reply div, with the review fields. Returns true\nif the comment is a review, and false if just a comment/reply.\n@param {Element} div Div of the whole comment (not the reply)\n@param {Element} replyDiv Div for the comment's reply or review form.\n@returns {object} // TODO typedef\n */\nfunction editableReview(div, replyDiv) {\n\tlet ratingElem = div.querySelector('.Rating')\n\tlet purchasedElem = div.querySelector('.Purchased')\n\tlet purchaseLocationElem = div.querySelector('.PurchaseLocation')\n\tlet ctfElem = div.querySelector('.Counterfeit')\n\n\tlet reviewObj = {\n\t\tis_review: false\n\t}\n\n\tlet templ = document.getElementById('submitReviewTemplate')\n\tif (templ === null) {\n\t\treturn reviewObj\n\t}\n\tlet tcopy = templ.content.cloneNode(true)\n\tlet review = tcopy.querySelector('div')\n\n\treview.querySelector('.card-header').textContent = \"Edit Review\"\n\treview.querySelector('#submitReviewBtn').remove()\n\n\tif (ratingElem !== null) {\n\t\treview.querySelector('.reviewStars').innerHTML = getStarsFromRating(ratingElem.dataset.value)\n\t\treviewObj.is_review = true\n\t\treviewObj.rating = ratingElem.dataset.value\n\t}\n\tif (purchasedElem !== null) {\n\t\treview.querySelector('.reviewPurchased').checked = purchasedElem.dataset.value\n\t\treviewObj.is_review = true\n\t\treviewObj.purchased = isTrue(purchasedElem.dataset.value)\n\t}\n\tif (purchaseLocationElem !== null) {\n\t\treview.querySelector('.reviewPurchaseLocation').value = purchaseLocationElem.dataset.value\n\t\treviewObj.is_review = true\n\t\treviewObj.purchase_location = purchaseLocationElem.dataset.value\n\t}\n\tif (ctfElem !== null) {\n\t\treview.querySelector('.reviewReportCounterfeit').checked = ctfElem.dataset.value\n\t\treviewObj.is_review = true\n\t\treviewObj.counterfeit = isTrue(ctfElem.dataset.value)\n\t}\n\tif (reviewObj.is_review) {\n\t\treplyDiv.append(review)\n\t\tvar stars = review.querySelectorAll(\".reviewStars span\")\n\t\tstars.forEach(star => {\n\t\t\tstar.addEventListener('click', function () { // Don't make anonymous, loses ability for `this` keyword.\n\t\t\t\treturn SetRatingStar(parseInt(this.getAttribute(\"data-rating\")), stars)\n\t\t\t})\n\t\t})\n\t}\n\treviewObj.element = review\n\treturn reviewObj\n}\n\n/**\nHelper function for setting reply and edit text and action listener.\nReturns whether the reply changed from an edit to a reply.\n@param {Element} div Div of the whole comment (not the reply)\n@param {boolean} edit Whether new reply or an edit\n@returns {boolean}\n */\nfunction setReplyFunc(div, edit) {\n\tedit = isBool(edit)\n\n\tlet change = false\n\tif (\n\t\t(div.classList.contains(\"reply\") && edit == true) ||\n\t\t(div.classList.contains(\"edit\") && edit == false)\n\t) {\n\t\tchange = true\n\t}\n\n\tlet textarea = div.querySelector('textarea')\n\tif (edit == true) {\n\t\ttextarea.value = div.querySelector('.comment').innerText\n\t\tdiv.classList.remove(\"reply\")\n\t\tdiv.classList.add(\"edit\")\n\t} else {\n\t\t// Reply\n\t\ttextarea.value = \"\"\n\t\tdiv.classList.remove(\"edit\")\n\t\tdiv.classList.add(\"reply\")\n\t}\n\treturn change\n}\n\n/**\nCreateSubmitReviewForm generates or regenerates the review form. Returns true on\nsuccess.\n@returns {boolean}\n */\nfunction CreateSubmitReviewForm() {\n\tlet tcopy = document.getElementById('submitReviewTemplate').content.cloneNode(true)\n\tlet review = tcopy.querySelector('div')\n\t// Add class to the main review form on the page, but not the template, since\n\t// edits may also be using the review template, but a main class must be used\n\t// for finding the main form on the page.\n\treview.classList.add(\"submitReview\")\n\tlet reviewForm = document.getElementById('reviewFormDiv')\n\treviewForm.innerHTML = \"\" // reset incase it was called before. \n\treviewForm.append(review)\n\tShow(reviewForm)\n\n\t///////////////////////\n\t// Review Star Javascript\n\t///////////////////////\n\tvar stars = review.querySelectorAll(\".reviewStars span\")\n\tstars.forEach(star => {\n\t\tstar.addEventListener('click', function () {\n\t\t\tvar rating = parseInt(this.getAttribute(\"data-rating\"))\n\t\t\treturn SetRatingStar(rating, stars)\n\t\t})\n\t})\n\n\treview.querySelector('#submitReviewBtn').addEventListener('click', function (event) {\n\t\tevent.preventDefault()\n\t\tSubmitReview()\n\t})\n\t// Shift + Enter should submit review. \n\treview.querySelector('.reviewComment').addEventListener('keydown', function (event) {\n\t\tif (event.code == \"Enter\" && event.shiftKey) {\n\t\t\tevent.preventDefault()\n\t\t\tSubmitReview()\n\t\t}\n\t})\n}\n\n// TODO not implemented. Future use.\n// statefulPage resets the page set after ajax actions.\nfunction statefulPage(page) {\n\tif (isEmpty(parsd.obj.rating)) {\n\t\tHide(document.getElementById('cyphrRating').parentElement)\n\t}\n\tSetPageRatingStar(parsd.obj.rating)\n\n\tif (parsd.obj.rating != 0) {\n\t\tlet ratingheader = document.querySelector('.cyphrme_rating')\n\t\tlet ratingSpan = document.getElementById('cyphrRating') // TODO Rename\n\t\t// TODO Make these accessible via Page.\n\t\t// Both need to be accessible, because one is for the integer, and one is for the stars\n\t\tif (ratingSpan !== null) {\n\t\t\tratingSpan.innerText = parsd.obj.rating\n\t\t}\n\t\tif (ratingheader !== null) {\n\t\t\tShow(ratingheader)\n\t\t\tShow(ratingheader.parentElement)\n\t\t}\n\t\tSetPageRatingStar(parsd.obj.rating)\n\n\t}\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Ajax Section\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nSubmitComment Submits top-level comments, replies and edits. 'topLevel' flag is\nused for submitting top level comments. See blog/md pages for example.\nThrows error if necessary components are not set.\n@param {Comment} comment\n@param {boolean} [topLevel=false]\n@returns {void}\n@throws {error}\n */\nasync function SubmitComment(comment, topLevel) {\n\t// console.debug(\"In SubmitComment\")\n\n\tif (isEmpty(Login.CozeKey.tmb)) {\n\t\tCyphrme.Error(\"No key set for this User.\")\n\t}\n\tlet cb = returnReplyCallback\n\tlet coze\n\tlet api\n\tif (comment.edit) {\n\t\tif (isEmpty(comment.id)) {\n\t\t\tthrow new Error(\"comment.SubmitComment: Comment ID must be set on edits.\")\n\t\t}\n\t\tcoze = await CVA.CommentUpdate(comment)\n\t\tapi = Cyphrme.API.Post.CommentUpdate\n\t} else {\n\t\tif (topLevel) {\n\t\t\tcb = async (parsd) => {\n\t\t\t\tif (isEmpty(parsd)) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif (!isEmpty(Login.AccountDetails.display_name)) {\n\t\t\t\t\tparsd.obj.cmt.display_name = Login.AccountDetails.display_name\n\t\t\t\t}\n\t\t\t\tdocument.getElementById('commentThread').prepend(createComment(parsd.obj.cmt))\n\t\t\t}\n\t\t} else {\n\t\t\tif (isEmpty(comment.parent)) {\n\t\t\t\tthrow new Error(\"comment.SubmitComment: Comment parent must be set.\")\n\t\t\t}\n\t\t\tdelete comment.id\n\t\t\tdelete comment.edit\n\t\t\tcomment.rtd = await getCommentTextDigest(comment.parent) // (RTD = reply to digest)\n\t\t}\n\n\t\tcoze = await CVA.CommentCreate(comment)\n\t\tapi = Cyphrme.API.Post.Comment\n\t}\n\n\t// console.debug(\"Testing: \", coze)\n\t// return\n\n\tlet formData = new FormData()\n\tformData.append('coze', JSON.stringify(coze))\n\tcb(await Ajax.FetchPost(api, formData))\n}\n\n/**\ngetCommentTextDigest accepts a comment's ID and returns the (B64) digest of the\ncomment's current text on the page. The hash alg used for the digest is\ndependent on the current selected CozeKey.\n@param {B64} id\n@returns {Dig}\n@throws {error} Fails when element does not exist.\n*/\nasync function getCommentTextDigest(id) {\n\tlet commentElem = document.getElementById(id)\n\tif (commentElem === null) {\n\t\tCyphrme.Error(\"Could not find comment for ID: \" + id) // throws\n\t}\n\treturn Lib.HashAB(await Lib.SToArrayBuffer(commentElem.querySelector('.comment').textContent), Login.CozeKey.alg)\n}\n\n/**\nreturnReplyCallback expects an object that contains comment reply or comment\nedit. Edit = true means the comment updates an existing comment/review. Edit =\nfalse means the comment is a reply and a new comment is added to the page.\nServer response holds `edit` to know if it was a reply or edit.\n@param {object} parsd Parsed server response.\n@returns {void}\n*/\nfunction returnReplyCallback(parsd) {\n\tlet comment = parsd.obj.cmt\n\tlet edit = parsd.obj.edit\n\tlet parent = Element\n\tif (!isTrue(edit)) {\n\t\t// Reply\n\t\tif (!isEmpty(Login.AccountDetails.display_name)) {\n\t\t\tcomment.display_name = Login.AccountDetails.display_name\n\t\t}\n\t\tparent = document.querySelector(\"div[data-commentid=\" + \"\\\"\" + comment.parent + \"\\\"\")\n\t\tlet card = createComment(comment)\n\t\tparent.append(card)\n\t} else {\n\t\t// Edit\n\t\tparent = document.querySelector(\"div[data-commentid=\" + \"\\\"\" + comment.id + \"\\\"\")\n\t\tparent.querySelector('.comment').innerText = comment.text\n\t\tparent.querySelector('.updated').innerText = new Date(comment.updated).toLocaleString()\n\n\t\t// Clear and set review fields.\n\t\tlet ce = parent.querySelector('.comment_extra')\n\t\tce.innerHTML = \"\"\n\t\tcreateExtraAppend(ce, \"Rating\", comment.rating)\n\t\tcreateExtraAppend(ce, \"Purchased\", isBool(comment.purchased))\n\t\tcreateExtraAppend(ce, \"Purchase Location\", comment.purchase_location)\n\t\tcreateExtraAppend(ce, \"Counterfeit\", comment.counterfeit_report)\n\t}\n\n\t// Removes existing reply div on success.\n\tparent.querySelector(\".replyDiv\").remove()\n\t// Hide No Comments incase shown.\n\tHide('noCommentsOnPage')\n}\n\n/**\nSubmitReview Submits a review on a page/product, and updates the product page\nwith the new review's rating calculated in. This function works for any page\nusing the comment/review template.\n@returns {void}\n */\nasync function SubmitReview() {\n\tlet pay = await setReviewPay(document.querySelector('.submitReview'))\n\tlet formData = new FormData()\n\tformData.append('coze', JSON.stringify(await CVA.CommentCreate(pay)))\n\t// formData.append('error', \"true\") // Debugging / testing\n\tlet parsd = await Ajax.FetchPost(Cyphrme.API.Post.Comment, formData)\n\t// console.debug(\"Parsd: \", parsd)\n\n\t// Start of \"callback\".\n\tlet commentThread = document.querySelector('#CommentSection')\n\tif (!isEmpty(parsd.obj) && !isEmpty(parsd.obj.cmt)) {\n\t\tHide(\"reviewFormDiv\")\n\n\t\tif (!isEmpty(Login.AccountDetails.display_name)) {\n\t\t\tparsd.obj.cmt.display_name = Login.AccountDetails.display_name\n\t\t}\n\n\t\tlet card = createReview(parsd.obj.cmt)\n\t\tcommentThread.append(card)\n\t\tShow(commentThread)\n\t\tCyphrme.JumpToAnchor(\"#\" + card.dataset.commentid)\n\t\tHide('noCommentsOnPage')\n\t}\n}\n\n\n/**\nsetReviewPay returns a populated pay object, with review fields set based on the\nreviewObj passed in. Other fields, such as child_ac is set from the PagRec\nglobal.\n@param {element} reviewDiv HTML element holding the review's fields/values.\n@param {object} [pay] Optional comment pay.\n@returns {Pay}\n */\nasync function setReviewPay(reviewDiv, pay) {\n\t// console.debug(\"Review Div: \", reviewDiv)\n\n\t// * TODO Use urlformjs to fetch fields.\n\t// console.debug(URLForm.GetForm(initedFormOptions))\n\tlet reviewObj = {\n\t\ttext: reviewDiv.querySelector('.reviewComment').value,\n\t\tcounterfeit: reviewDiv.querySelector('.reviewReportCounterfeit').checked,\n\t\tpurchase_location: reviewDiv.querySelector('.reviewPurchaseLocation').value,\n\t\tpurchased: reviewDiv.querySelector('.reviewPurchased').checked,\n\t\trating: reviewDiv.querySelectorAll('.reviewStars .bi-star-fill').length,\n\t}\n\t// console.debug(\"Review Object: \", reviewObj)\n\n\t// Create review, sorted by UTF-8.\n\t// NOTE, if fields are built out of order, the message is sent to the server\n\t// in the order it is built in, and the signature does not not verify.\n\tif (isEmpty(pay)) {\n\t\t/**@type {Comment} */\n\t\tpay = {}\n\t}\n\tif (!isEmpty(reviewObj.text)) {\n\t\tpay.text = reviewObj.text\n\t}\n\tif (!isEmpty(reviewObj.counterfeit)) {\n\t\tpay.counterfeit_report = reviewObj.counterfeit\n\t}\n\t// console.debug(PR)\n\tpay.root = PR.pag.subject_value\n\tif (!isEmpty(reviewObj.purchased)) {\n\t\tpay.purchased = reviewObj.purchased\n\t}\n\tif (!isEmpty(reviewObj.purchase_location)) {\n\t\tpay.purchase_location = reviewObj.purchase_location\n\t}\n\tif (!isEmpty(reviewObj.rating)) {\n\t\tpay.rating = parseFloat(reviewObj.rating)\n\t\tpay.rating_denom = 5\n\t}\n\tif (!isEmpty(PR.pag.child_ac)) {\n\t\tpay.child_ac = PR.pag.child_ac\n\t}\n\treturn pay\n}\n\n/**\ncommentDelete is for deleting comments and reviews.\n\nA div of the review/comment being deleted is passed in, and then sent as a\nform to the server to be deleted.\n\nRemoves the review/comment from the page if delete is successful.\n@param {Element} commentElem HTML Div of the entire review/comment being deleted.\n@returns {void}\n */\nasync function commentDelete(commentElem) {\n\tlet formData = new FormData()\n\tformData.append('coze', JSON.stringify(await CVA.CommentDelete(commentElem.dataset.commentid)))\n\t// formData.append('error', \"true\") // Testing / debugging\n\tlet parsd = await Ajax.FetchPost(Cyphrme.API.Post.CommentDelete, formData)\n\tif (isEmpty(parsd)) {\n\t\treturn\n\t}\n\n\tlet parent = document.querySelector(\"div[data-commentid=\" + \"\\\"\" + parsd.obj.id + \"\\\"\")\n\tif (isEmpty(parsd.obj.cmt)) {\n\t\tparent.remove()\n\t} else {\n\t\t// Mark as deleted\n\t\tparent.classList.add('border-warning')\n\n\t\tparent.querySelector('.text-muted').remove()\n\t\tparent.querySelector('.comment').remove()\n\t\tparent.querySelector('.buttons').remove()\n\n\t\tlet reviewDetails = parent.querySelector('.row')\n\t\tif (!isEmpty(reviewDetails)) {\n\t\t\treviewDetails.remove()\n\t\t}\n\t\tparent.prepend(\"Comment has been deleted\")\n\t}\n\n\tif (document.querySelector('#CommentSection').querySelector('.comment') == null) {\n\t\tconsole.debug(\"#CommentSection is empty. Hiding.\")\n\t\tHide(document.querySelector('#CommentSection'))\n\t}\n\n\tToggleReviewForm(PR.rec, parsd.obj.id)\n}\n\n/**\nToggleReviewForm checks if any of the given record's uad matches the current\nLogin.UAD, or current Application CozeKey tmb. If any record is a match, the\nreview form div is hidden, as to not allow multiple review submissions by a\nsingle user (from the GUI). Checks for non-authed users, and does not show if\nthey are not logged in.\n@param {Comment[]} records\n@param {B64} [id] Optional ID for skipping over.\n@returns {void}\n */\nfunction ToggleReviewForm(records, id) {\n\tlet hide = false\n\tif (!isEmpty(records)) {\n\t\tfor (let i = 0; i < records.length; i++) {\n\t\t\tif (records[i].id == id) { // for deletes\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlet uad = records[i].uad\n\t\t\tif (uad == Login.UAD || uad == Login.CozeKey.tmb) {\n\t\t\t\thide = true\n\t\t\t}\n\t\t}\n\t}\n\tif (!hide) {\n\t\t// If user is not logged in, display email invite form.\n\t\tif (isEmpty(Login.UAD)) {\n\t\t\tLogin.CreatePreInviteEmailForm()\n\t\t} else {\n\t\t\tCreateSubmitReviewForm()\n\t\t}\n\t}\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Star Section\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nSetRatingStar sets the review form submission rating and HTML star elements\nin the GUI.\n@param {number} rating Rating value.\n@returns {void}\n */\nfunction SetRatingStar(rating, stars) {\n\tfor (var i = 0; i < stars.length; i++) {\n\t\tif (i < rating) {\n\t\t\tstars[i].innerHTML = ''\n\t\t} else {\n\t\t\tstars[i].innerHTML = ''\n\t\t}\n\t}\n}\n\n/**\nSetPageRatingStar sets the main rating of the page.\n@param {number} rating Rating value\n@returns {void}\n */\nasync function SetPageRatingStar(rating) {\n\t// Sanitize NaN\n\tif (isEmpty(rating)) {\n\t\trating = 0\n\t}\n\t// console.debug(rating)\n\t/** @property {Element} stars - The rating star div. */\n\tlet stars = document.querySelector('.cyphrStars')\n\n\t// Stars not supported on some ACs (e.g. markdown).\n\tif (stars === null) {\n\t\treturn\n\t}\n\n\tif (rating == 0) {\n\t\tHide(stars)\n\t\treturn\n\t}\n\tShow(stars)\n\n\t// Add to the page\n\tstars.innerHTML = getStarsFromRating(rating)\n\treturn\n}\n\n/**\ngetStarsFromRating returns HTML with stars populated from the given rating.\n@param {number} rating Rating value\n@returns {HTMLElement}\n */\nfunction getStarsFromRating(rating) {\n\t// Rounds anything under .75 and over .25 to to .5, everything else to nearest integer.\n\trating = (Math.round(rating * 2) / 2).toFixed(1)\n\tvar starhtml = \"\"\n\tvar starNum = 0\n\n\tfor (var i = .5; i <= rating; i += .5) {\n\t\tif (i % 1 == 0) {\n\t\t\t// data-rating is for user submittable ratings, and currently, half stars\n\t\t\t// are not supported for submitting ratings.\n\t\t\tstarhtml += ''\n\t\t\tstarNum++\n\t\t} else if (i == rating) {\n\t\t\tstarhtml += ''\n\t\t\tstarNum++\n\t\t}\n\t}\n\twhile (starNum < 5) {\n\t\tlet dataRating = starNum + 1 // Don't do within string, concats 1 as a string.\n\t\tstarhtml += ''\n\t\tstarNum++\n\t}\n\treturn starhtml\n}", "\"use strict\";\n\nexport {\n\tErr,\n}\n\nconst Err = {\n\tAccountIDNotSet: \"Account ID not set. Please make sure you are using an activated account.\"\n}\n\n\n// TODO thinking about this: \n// function CheckUAD() {\n// \tif (isEmpty(App.UAD)) {\n// \t\tApp.Error(App.Err.AccountIDNotSet);\n// \t}\n// }", "\"use strict\";\n\nimport * as Cyphrme from './cyphrme.js';\nimport * as Cva from './cva.js';\nimport * as Login from './login.js';\nimport * as Enum from './error_enum';\nimport * as Ajax from './ajax.js';\n\nexport {\n\tDownloadWallet,\n\tEmailWallet,\n\tUpsertKey,\n}\n\n/**\n@typedef {import('./cyphrme.js').CozeKey} CozeKey\n */\n\n\n/**\nDownloadWallet takes Keys in memory and downloads them all in a JSON file.\n@param {object} keys Keys to download.\n@param {string} [extra] Extra identifier for filename.\n@returns {void}\n */\nasync function DownloadWallet(keys, extra) {\n\tif (!isEmpty(extra)) {\n\t\tvar filename = \"CyphrMe_Key_Wallet_\" + extra + \"_\" + Date.now() + \".json\";\n\t} else {\n\t\tvar filename = \"CyphrMe_Key_Wallet_\" + Date.now() + \".json\";\n\t}\n\n\t// Download link\n\tlet downloadLink = document.createElement(\"a\");\n\tdownloadLink.style.display = \"none\";\n\tdownloadLink.download = filename;\n\tdownloadLink.href = window.URL.createObjectURL(new Blob([JSON.stringify(keys, null, 1)], {\n\t\ttype: \"application/json\"\n\t}));\n\tdocument.body.appendChild(downloadLink);\n\n\t// Simulate a click.\n\tdownloadLink.click();\n};\n\n/**\nEmailWallet takes Keys in memory and emails them all to the given email.\n@param {string} email Email to send wallet to.\n@param {object} keys Keys to download.\n@returns {void}\n */\nasync function EmailWallet(email, keys) {\n\tlet formData = new FormData()\n\tformData.append('wallet', JSON.stringify(keys, 0, 1)) // Send backup of entire wallet.\n\t// Encapsulate Coze with `email_verify`, as Email Cozies have special\n\t// unmarshal logic for the PreInvite User Onboarding process.\n\tformData.append(\"coze\", JSON.stringify({\n\t\t\"email_verify\": await Cva.EmailBackupCreate(email)\n\t}))\n\tformData.append(\"email\", Login.AccountDetails.email)\n\tlet resp = await Ajax.FetchPost(Cyphrme.API.Post.EmailPrivateKeyBackup, formData)\n\tconsole.debug(\"Response: \", resp)\n}\n\n/**\nUpsertKey upserts the given key.\n@param {CozeKey} ck Key to be upserted.\n@param {function} callback Callback function to be executed on response.\n@returns {void}\n */\nasync function UpsertKey(ck, callback) {\n\t// Sanitize for only fields that should be signed over.\n\tlet k = {\n\t\talg: ck.alg,\n\t\ttmb: ck.tmb,\n\t}\n\tif (!isEmpty(ck.iat)) {\n\t\tk.iat = ck.iat\n\t}\n\tif (!isEmpty(ck.x)) {\n\t\tk.x = ck.x\n\t}\n\tif (!isEmpty(ck.kid)) {\n\t\tk.kid = ck.kid\n\t}\n\n\tif (isEmpty(Login.UAD)) {\n\t\tCyphrme.Error(Enum.Err.AccountIDNotSet)\n\t}\n\tlet formData = new(FormData)\n\tformData.append('coze', JSON.stringify(await Cva.KeyUpsert(ck)))\n\tcallback(await Ajax.FetchPost(Cyphrme.API.Post.UpsertKey, formData))\n}", "\"use strict\"\n\nimport * as Cyphrme from './cyphrme.js'\nimport * as Cva from './cva.js'\nimport * as Ajax from './ajax.js'\nimport * as Comment from './comment.js'\nimport * as Wallet from './wallet.js'\n\nimport * as Coze from '../pkg/coze_all~fv=-7uCIHNa.min.js'\nimport '../pkg/urlform~fv=VhWyOSvq.min.js' // Namespaced as 'URLForm'.\n\n/**\n@typedef {import('../../../pkg/cozejs/typedef.js').Pay} Pay\n@typedef {import('../../../pkg/cozejs/typedef.js').Coze} Coze\n@typedef {import('../../../pkg/cozejs/typedef.js').Key} Key\n@typedef {import('../../../pkg/cozejs/typedef.js').PublicCozeKey} PublicCozeKey\n@typedef {import('../../../pkg/cozejs/typedef.js').PrivateCozeKey} PrivateCozeKey\n@typedef {import('../../../pkg/cozejs/typedef.js').B64} B64\n@typedef {import('../../../pkg/cozejs/typedef.js').Time} Time\n@typedef {import('../../../pkg/cozejs/typedef.js').Tmb} Tmb\n@typedef {import('./ajax.js').SuccessJSON} SuccessJSON\n */\n\n/**\nAccountDetails holds the account information for a user.\n\n- id: ID of the account. Should match 'uad' after activation.\n- uad: Owner of the account (notary until user activation).\n- activated: Time when the user first logged in to their account on Cyphr.me.\n- backup: If any account key has been backed up. Used to make sure users cannot get locked out of their account. \n- notary: User that added this user to the system.\n- display_name: Display name for the account.\n- updated: Last time the account was updated.\n- first_name: User's first name.\n- last_name: User's last name.\n- address_1: User's (home) address.\n- address_2: User's second (home) address.\n- country: Country user resides in.\n- invtes: How many user invites this account has.\n- email: Email address for the account.\n- phone_1: Phone number for the account.\n- phone_2: Second phone number for the account.\n- city: City for the account.\n- state: State for the account.\n- zip: Zip/area code for the account.\n- email_recovery: Can the account be recovered through email?\n- email_verified: The primary email in use has been verified.\n@typedef {object} AccountDetails\n@property {B64} id\n@property {B64} uad\n@property {Time} activated\n@property {Bool} backup \n@property {B64} notary\n@property {string} [display_name]\n@property {Time} [updated]\n@property {string} [first_name]\n@property {string} [last_name]\n@property {string} [address_1]\n@property {string} [address_2]\n@property {string} [country]\n@property {number} [invites]\n@property {string} [email]\n@property {string} [phone_1]\n@property {string} [phone_2]\n@property {string} [city]\n@property {string} [state]\n@property {string} [zip]\n@property {boolean} [email_recovery]\n@property {boolean} [email_verified]\n */\n\n/**\nProfile is the user updatable AccountDetails fields for a user's account.\n** 'id' is required in the object, and is not updatable. **\nSee `AccountDetails`.\n\n- display_name: Defaults to the abbreviated id (e.g. cLj8vs...).\n\nRest of the fields are self explanatory and fairly standard for most user forms.\nCaveats, such as max lengths, restrictions, etc. should be doc'd if relevant for\nthe field.\n@typedef {object} Profile\n@property {B64} id\n@property {string} [display_name]\n@property {string} [first_name]\n@property {string} [last_name]\n@property {string} [email]\n@property {string} [address_1]\n@property {string} [address_2]\n@property {string} [phone_1]\n@property {string} [phone_2]\n@property {string} [city]\n@property {string} [state]\n@property {string} [zip]\n@property {string} [country]\n */\n\n/**\nAccounts holds a key:value pair with the name of the account as the key, and\nthe AccountDetails object for the account as the value.\n@typedef {object} Accounts\n */\n\n\nCyphrme.AddOnloadFirst(LoginOnload)\n\nexport {\n\tUAD,\n\tAccounts,\n\tAccountDetails,\n\tCozeKey,\n\tCozeKeyPublic,\n\tCozeKeyExtra,\n\tKeys,\n\n\tLS_AccountDetails,\n\tLS_Keys,\n\tLS_SelectedKey,\n\tLS_Accounts,\n\n\tAccountDetailsCallback,\n\tClearLocalStorage,\n\tCozeKeyNormal,\n\tDeleteLocalAccount,\n\tDeleteKey,\n\tGetProfile,\n\tGlobalNewKey,\n\tInitAccounts,\n\tInitFromStorage,\n\tInitKeys,\n\tIsUserBackedUp,\n\tLogin,\n\tLogout,\n\tIsLoggedIn,\n\tCreatePreInviteEmailForm, // Email onboard\n\tSetLocalAccount,\n\tSetSelectedKey,\n\tSetLocalAccounts,\n\tUpdateKid,\n\tUpsertGlobalKey,\n\tUpsertGlobalKeys,\n\tUpdateLocalAccountDisplayName,\n\tUpdateAccountDisplayNames,\n\tUpsertGlobalAccounts,\n\tUpdateAccount,\n\tVerifyAccess,\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Global vars set by the application\n////////////////////////////////////////////////////////////////////////////////\n\n// Why both Coze and CryptoKeys? Because Coze is used for human and wire\n// transmission. CryptoKey are used to actually perform Crypto. Both need to be\n// set for the application to work as expected.\n\n/** UAD is the unique identifier used for users accounts. It might not be a\nvalid key tmb and instead may be a `wad` \"wallet digest\"\n * @type {B64} **/\nvar UAD = \"\"\n\n/** @type {AccountDetails} **/\nvar AccountDetails = {} // Account details object. All details in a user's \"account\".\n\n/** @type {PrivateCozeKey} **/\nvar CozeKey = {} // The selected Key. \n\n/** @type {PrivateCozeKey} **/\nvar CozeKeyExtra = {} // The select key with any \"extra\" fields like `\"SerializedTmb\":\"ES256:b3Td\"`\n\n/** @type {PublicCozeKey} **/\nvar CozeKeyPublic = {} // Public Coze Key javascript object.\n\n// TODO typedef\nvar Keys = {} // All user keys.\n\n/** @type {Accounts} **/\nvar Accounts = {} // All accounts stored locally.\n\n// LoginKeySuccess is a global to avoid callback hell on login. \nvar LoginKeySuccess\n\nvar preInviteModal = \"\" // Bootstrap HTML modal for pre-invites.\n\n// Error enum from server when pre-invite account has already been created and\n// a replay is attempted.\nconst errPreInviteAccountAlreadyCreated = \"Pre invite account has already been created, you must login from the wallet page.\"\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Local Storage\n////////////////////////////////////////////////////////////////////////////////\n\n// \"Current\" or private key being used to sign things. It is the default, or\n// currently selected key used for interacting with the application.\nconst LS_SelectedKey = \"selected_key\"\nconst LS_Keys = \"keys\" // All keys in local storage, including non uploaded keys.\nconst LS_AccountDetails = \"account_details\" // The current local account details.\nconst LS_Accounts = \"accounts\" // All of the different local accounts.\n\n\nasync function LoginOnload() {\n\tconsole.log(\"LoginOnload\")\n\tawait InitFromStorage()\n\t// Add account to links on the page \n\tfor (var l of document.querySelectorAll('.append_account_to_link')) {\n\t\tif (!isEmpty(UAD)) {\n\t\t\tl.href += \"/\" + UAD\n\t\t} else {\n\t\t\tHide(l)\n\t\t}\n\t}\n\tpreinviteAndBackupPesterModal()\n\n\t//// Login Debug:\n\t// console.log(\"UAD: \", UAD)\n\t// console.log(\"AccountDetails: \", JSON.stringify(AccountDetails))\n\t// console.log(\"CozeKey: \", JSON.stringify(CozeKey))\n\t// console.log(\"CozeKeyPublic: \", JSON.stringify(CozeKeyPublic))\n}\n\n/**\nLogin uses a Coze Key to generate a login request and key upsert and send the\ncozies to the server. \"key_upsert\" cozies are always sent along with login\nrequests. We cannot perform logic on key fields, such as `time` or `key_added`,\nbecause we have no guarantee that the database is in sync with the local key.\nAlthough \"key_upsert\" cozies are sent along with every request, the server only\nuses it on first time login requests.\n\n{\n\"login_request\": object of type `cyphr.me/user/login/create`,\n\"key_upsert\": Object of type cyphr.me/key/upsert,\n}\n\nOptional formData allows additional fields to be sent along in the form.\nSee `generatePreInviteAccount` for an example.\n\nTODO change login parameters to LoginObject, super type of UploadObj.\n@param {PrivateCozeKey} cozeKey\n@param {function} [callback]\n@param {FormData} [formData]\n@returns {void}\n@throws {error}\n */\nasync function Login(cozeKey, callback, formData) {\n\tLoginKeySuccess = cozeKey\n\tif (isEmpty(formData)) {\n\t\tformData = new(FormData)\n\t}\n\ttry {\n\t\tformData.append('login_request', await genLoginRequest(cozeKey))\n\t\t// Second cozeKey is used to sign the coze, instead of App CozeKey.\n\t\tformData.append('key_upsert', JSON.stringify(await Cva.KeyUpsert(cozeKey, cozeKey)))\n\n\t\tawait loginCallback(await Ajax.FetchPost(Cyphrme.API.Post.Login, formData), callback)\n\t} catch (e) {\n\t\t// console.debug(e)\n\t\tCyphrme.Error(e)\n\t}\n}\n\n/**\ngeneratePreInviteAccount checks for the `verify_preinvite_modal` keyword in the\nURL Query, and if present, displays the Pre-Invite Account modal (sent by the\nserver), and calls login. Success/failures are displayed in the modal.\n@returns {void}\n */\nasync function generatePreInviteAccount() {\n\tlet quags = URLForm.GetURLKeyValue(URLForm.GetDefaultFormOptions())\n\t// console.log(\"generatePreInviteAccount quags\", quags)\n\tif (!(\"email_verify\" in quags)) {\n\t\tApp.Error(\"Pre-Invite account generation started, but Coze not in URL.\")\n\t\treturn\n\t}\n\t// If user has not yet created a key, generate and set new key for signing.\n\tif (isEmpty(CozeKey)) {\n\t\tCozeKey = await GlobalNewKey(Coze.Algs.ES256)\n\t\tawait SetSelectedKey(CozeKey)\n\t}\n\n\t// Send login create coze, as well as key upsert coze along with verification,\n\t// and cookie should be send back immediately so user is logged in and can\n\t// leave comment.\n\tlet formData = new FormData()\n\tformData.append('email_verify', quags[\"email_verify\"])\n\tLogin(CozeKey, null, formData)\n}\n\n\n/**\nloginCallback is the callback for login that can be performed on any page.\nParameter \"callback\" is called at the end of the function.\n@param {SuccessJSON} parsd Server response\n@param {function} callback Callback function\n@returns {void}\n */\nasync function loginCallback(parsd, callback) {\n\tconsole.log(\"loginCallback\", parsd)\n\tif (isEmpty(parsd)) {\n\t\tCyphrme.Error(\"Login failed.\")\n\t}\n\tif (!parsd.success) {\n\t\t// Check to see if error is from trying to replay the pre-invite account\n\t\t// creation login request.\n\t\tif (parsd.msg == errPreInviteAccountAlreadyCreated) {\n\t\t\tShow(\"closePreInvModalBtn\") // In case the modal is still present, show the close button.\n\t\t\tpreInviteModal.hide()\n\t\t\treturn\n\t\t}\n\t\tCyphrme.Error(parsd.msg) // Throws\n\t}\n\n\ttry {\n\t\t//// Debugging\n\t\t// console.debug(parsd)\n\t\t// return\n\n\t\t// If any \"create accounts\" buttons are still visible, hide them.\n\t\tdocument.querySelectorAll(\".createAccountBtn\").forEach((elem) => {\n\t\t\tHide(elem)\n\t\t})\n\n\t\t// Ensure user account was sent back in login response.\n\t\tif (isEmpty(parsd.obj.user)) {\n\t\t\tconsole.error(\"User's account was not sent back from the server response.\")\n\t\t\tCyphrme.Error(\"User not found in response.\")\n\t\t}\n\t\t// Ensure login token was sent back in login response.\n\t\tif (isEmpty(parsd.obj.login_token)) {\n\t\t\tconsole.error(\"Login token was not sent back from the server response.\")\n\t\t\tCyphrme.Error(\"Login token not found in response.\")\n\t\t}\n\n\t\tawait SetLocalAccount(parsd.obj.user)\n\t\tawait InitFromStorage()\n\t\tCyphrme.Notification('Login successful.', 'success')\n\t\t// console.log(LoginKeySuccess)\n\t\t// Key.time is used downstream to know if a key has been added to Cyphr.me. \n\t\t// Login responses do not send `key.time` so for first time logins use `iat`\n\t\t// for `key.time` and set it. It is set accurately when key is synced.\n\t\tif (isEmpty(LoginKeySuccess.time)) {\n\t\t\tLoginKeySuccess.time = parsd.obj.login_token.pay.iat\n\t\t}\n\n\t\tLoginKeySuccess.uad = parsd.obj.login_token.pay.uad\n\t\tawait SetSelectedKey(LoginKeySuccess) // throws\n\t\tpreinviteAndBackupPesterModal()\n\n\t\t// TODO move to comment.\n\t\t// Show review form div if template exists on page.\n\t\tif (document.querySelector('#submitReviewTemplate') !== null) {\n\t\t\tComment.CreateSubmitReviewForm()\n\t\t}\n\t\tHide(\"emailFormDiv\")\n\n\t\tif (typeof callback == \"function\") {\n\t\t\treturn callback(parsd)\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\"loginCallback: \", error)\n\t\tCyphrme.Error(error)\n\t}\n}\n\n/**\nLogout expires the current cookie, as well as the global application variables\n(uad, cozekey, cozekey public), as well as the local browser storage for the\napplication (account_details). Takes an optional 'noRedirect' param that should\nbe passed as 'true' if needing to stay on the same page after logging out.\n@param {boolean} [noRedirect=false] If true, logout without redirecting to home.\n@returns {void}\n */\nasync function Logout(noRedirect) {\n\t// console.debug(noRedirect)\n\n\t// Expire the cookie\n\tdocument.cookie = \"login_token= expires=Thu, 01 Jan 1970 00:00:00 UTC path=/\"\n\t// console.debug(document.cookie)\n\tUAD = null\n\tCozeKey = null\n\tCozeKeyPublic = null\n\tlocalStorage.removeItem(LS_AccountDetails)\n\tlocalStorage.removeItem(LS_SelectedKey)\n\t// Forces page refresh to home page.\n\tif (isEmpty(noRedirect) || !noRedirect) {\n\t\twindow.location.href = \"https://\" + window.location.host\n\t}\n\tCyphrme.Notification('Successfully logged out.', 'success')\n}\n\n/**\nIsLoggedIn returns if the user is logged in as tracked by the local state.\n@returns {boolean}\n*/\nfunction IsLoggedIn() {\n\treturn (!isEmpty(UAD))\n}\n\n/**\ngenLoginRequest generates a new login request (for a token) with the given Coze\nKey. Used by the user to send authenticated requests to the server. Returns\nLoginRequestCoze as a string.\n\nTODO maybe move to CVA?\n@param {PrivateCozeKey} cozeKey\n@returns {string}\n@throws {error}\n */\nasync function genLoginRequest(cozeKey) {\n\tlet pay = {\n\t\talg: cozeKey.alg,\n\t\tiat: Now(),\n\t\ttmb: cozeKey.tmb,\n\t\ttyp: \"cyphr.me/user/login/create\",\n\t}\n\tlet coze = await Coze.Sign({\n\t\t\tpay: pay,\n\t\t},\n\t\tcozeKey)\n\t// No private\n\tcoze.key = {\n\t\t...cozeKey\n\t}\n\tdelete coze.key.d\n\treturn JSON.stringify(coze)\n}\n\n\n/**\npreinviteAndBackupPesterModal checks if the pre-invite setup or pester modal\nneeds to be triggered. It gets the pre-invite HTML via ajax so that that it\nisn't loaded on all Cyphr.me pages.\n\nThree testing cases:\n1. Not logged in.\n2. Page behavior on first time login\n3. logged in. \n4. URLFormJS variable `verify_preinvite_modal`\n@returns {void}\n*/\nasync function preinviteAndBackupPesterModal() {\n\tconsole.log(\"preinviteAndBackupPesterModal IsUserBackedUp:\", IsUserBackedUp())\n\n\tif (\"verify_preinvite_modal\" in URLForm.GetURLKeyValue()) {\n\t\tvar hasPreInviteModal = true\n\t}\n\t// Hasn't made an account (no UAD), Not logged in, or not invite present and invite not used.\n\tif (isEmpty(UAD) || IsUserBackedUp() && !hasPreInviteModal) {\n\t\treturn\n\t}\n\tconsole.warn(\"Calling Ajax PreInviteAccountModal. This should only happen if user account is not backed up or URL variable verify_preinvite_modal is set.\");\n\tlet response = await Ajax.FetchHTML(Cyphrme.Page.PreInviteAccountModal)\n\tlet div = document.createElement('div')\n\tdiv.innerHTML = response\n\tdocument.body.appendChild(div)\n\tpreInviteModal = new bootstrap.Modal(document.querySelector('#preInviteAccountModal'))\n\tpreInviteModal.show()\n\n\tif (hasPreInviteModal && !IsUserBackedUp()) {\n\t\tgeneratePreInviteAccount()\n\t\treturn\n\t}\n\n\t// Sanitize Coze Key to include only minimum fields.\n\tlet ck = {\n\t\talg: CozeKey.alg,\n\t\tiat: CozeKey.iat,\n\t\tkid: CozeKey.kid,\n\t\tx: CozeKey.x,\n\t\td: CozeKey.d,\n\t\ttmb: CozeKey.tmb,\n\t\tuad: UAD,\n\t}\n\n\tlet accountModal = document.querySelector('#preInviteAccountModal')\n\t// console.log(accountModal)\n\tlet modalContent = accountModal.querySelector('.modal-content')\n\tmodalContent.querySelector('.newKey').textContent = JSON.stringify(ck, 0, 1)\n\n\taccountModal.querySelector('.copyBtn').addEventListener('click', () => {\n\t\tClipboardE(modalContent.querySelector('.newKey'))\n\t\tCyphrme.Notification(\"Copied!\", \"success\")\n\t})\n\n\taccountModal.querySelector('.showPrivateKeyBtn').addEventListener('click', () => {\n\t\tToggleVisible(accountModal.querySelector('.privateKey'))\n\t})\n\n\tif (isEmpty(AccountDetails.email)) {\n\t\t// if (isEmpty(AccountDetails.email) || isEmpty(AccountDetails.email_verified) || !AccountDetails.email_verified) {\n\t\taccountModal.querySelector('#emailBackup').checked = false\n\t\tHide(accountModal.querySelector('.emailBackupRow'))\n\t} else {\n\t\taccountModal.querySelector('.emailSendAddress').textContent = AccountDetails.email\n\t}\n\n\t// TODO QR code for key\n\tHide(accountModal.querySelector(\"#CreateSpinner\"))\n\tHide(accountModal.querySelector(\"#closePreInvModalBtn\"))\n\tShow(accountModal.querySelector(\"#requiredBackup\"))\n\n\t// Check if any private key is not currently backed up.\n\tInitKeys()\n\tvar notBackedUp = {}\n\tvar currentWallet = {}\n\t// console.debug(\"UAD: \", UAD, \"Keys: \", Keys)\n\tfor (let k in Keys) {\n\t\tlet key = Keys[k]\n\t\t// Revokes keys cannot be updated, and only keys in the current logged in\n\t\t// wallet are checked.\n\t\tif (UAD === key.uad) {\n\t\t\tcurrentWallet[k] = key\n\t\t} else {\n\t\t\tcontinue\n\t\t}\n\t\tif (key.rvk > 0) {\n\t\t\tcontinue\n\t\t}\n\t\tif ((isEmpty(key.backup) || !key.backup) && !isEmpty(key.d)) {\n\t\t\tnotBackedUp[k] = key\n\t\t}\n\t}\n\t// console.debug(notBackedUp)\n\n\t// If already backed up, do not display the backup pester options.\n\tif (Object.keys(notBackedUp).length <= 0) {\n\t\tdocument.querySelector(\"#BackupPesterHeader\").textContent = \"Your account is already backed up.\"\n\t\tHide(\"BackupPester\")\n\t\tShow(\"closePreInvModalBtn\")\n\t\t// Set backup true on account.\n\t\tAccountDetails.backup = true\n\t\tSetLocalAccount(AccountDetails)\n\t\treturn\n\t}\n\n\taccountModal.querySelector('#requiredPreInvBackupBtn').addEventListener('click', async () => {\n\t\tlet atLeastOneChecked = false\n\t\tif (modalContent.querySelector('#emailBackup').checked) {\n\t\t\tif (isEmpty(AccountDetails.email)) {\n\t\t\t\tCyphrme.Error('Email could not be found for account.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait Wallet.EmailWallet(AccountDetails.email, currentWallet)\n\t\t\tatLeastOneChecked = true\n\t\t}\n\n\t\t// Download all local keys.\n\t\tif (modalContent.querySelector('#downloadBackup').checked) {\n\t\t\tawait Wallet.DownloadWallet(currentWallet)\n\t\t\tatLeastOneChecked = true\n\t\t}\n\n\t\tif (!atLeastOneChecked) {\n\t\t\tCyphrme.Error(\"At least one backup option must be selected.\")\n\t\t}\n\t\t// Send Key Upserts for marking keys as backed up.\n\t\tfor (let k in notBackedUp) {\n\t\t\tlet key = notBackedUp[k]\n\t\t\tkey.backup = Now()\n\t\t\tWallet.UpsertKey(key, async (parsd) => {\n\t\t\t\tKeys[parsd.obj.id].backup = parsd.obj.backup\n\t\t\t\t// Update keys within callback after each success, to ensure that\n\t\t\t\t// update does not occur before all ajax requests have completed.\n\t\t\t\tUpsertGlobalKeys()\n\t\t\t\tCyphrme.Notification('Key has been updated', 'success')\n\t\t\t})\n\t\t}\n\t\t// Set backup true on account.\n\t\tAccountDetails.backup = true\n\t\tSetLocalAccount(AccountDetails)\n\t\tShow(\"closePreInvModalBtn\")\n\t})\n}\n\n\n\n/**\nIsUserBackedUp recalculates and returns whether the current user's local\nstate is backed up.\n@returns {boolean} userIsBackedUp\n */\nfunction IsUserBackedUp() {\n\t// Backups are stored on keys, and only on the user's account locally.\n\t// Recalculate whether the user's account state is backed up.\n\t// Only keys in the current logged in wallet is checked.\n\tInitKeys()\n\t// console.debug(\"UAD: \", UAD,\"Keys: \", Keys)\n\n\tif (isEmpty(UAD)) {\n\t\treturn false\n\t}\n\n\t// Accounts with no keys are considered to be backed up.\n\tif (Object.keys(Keys).length <= 0) {\n\t\treturn true\n\t}\n\tlet userIsBackedUp = true\n\tfor (let k in Keys) {\n\t\tlet key = Keys[k]\n\t\t// Revoked keys, thumbprint only keys, and keys not yet in the system do not\n\t\t// need to be backed up.\n\n\t\t// If no UAD, the key is not in the system and doesn't need to be backed up.\n\t\tif (isEmpty(key.uad) || UAD !== key.uad) {\n\t\t\tcontinue\n\t\t}\n\t\t// If key.time is not set, the key is not in the system and doesn't need to be backed up. \n\t\tif (isEmpty(key.time) || key.time <= 0) {\n\t\t\tcontinue\n\t\t}\n\t\tif (key.rvk > 0) { // Revoked do not need backed up. \n\t\t\tcontinue\n\t\t}\n\t\tif (key.niw) { // Key is not in the current wallet and not of concern. \n\t\t\tcontinue\n\t\t}\n\t\tif (!isEmpty(key.backup) && key.backup > 0) {\n\t\t\tcontinue\n\t\t}\n\t\tconsole.debug(\"Key not backed up: \", key)\n\t\tuserIsBackedUp = false\n\t\tbreak\n\t}\n\n\treturn userIsBackedUp\n}\n\n/**\nCreatePreInviteEmailForm generates the email invite form from a template, and\ndisplays it on the page. Returns whether generation was successful.\n@returns {boolean}\n */\nfunction CreatePreInviteEmailForm() {\n\tlet emailForm = document.getElementById('emailFormDiv')\n\tShow(emailForm)\n\n\temailForm.querySelector('#submitEmailBtn').addEventListener('click', function (event) {\n\t\tsubmitPreInviteEmail()\n\t})\n\t// Shift + Enter should submit email.\n\temailForm.querySelector('#reviewPreInvEmail').addEventListener('keydown', function (event) {\n\t\tif (event.code == \"Enter\" && event.shiftKey) {\n\t\t\tsubmitPreInviteEmail()\n\t\t}\n\t})\n}\n\n/**\nsubmitPreInviteEmail submits a pre invite email creation request.\n@returns {void}\n */\nasync function submitPreInviteEmail() {\n\t// Non-authenticated users are assumed to be pre-invited users.\n\t// NOTE: Email validation is done server side.\n\tlet email = document.getElementById('reviewPreInvEmail').value\n\tlet displayName = document.getElementById('preInviteDisplayName').value\n\tif (isEmpty(email)) {\n\t\tCyphrme.Error(\"Email address is required.\")\n\t\treturn\n\t}\n\tlet formData = new FormData()\n\tformData.append('email', email)\n\tformData.append('display_name', displayName)\n\tformData.append('referrer', Cyphrme.Site + window.location.pathname) // URL from where request originated from.\n\ttry {\n\t\tlet resp = await Ajax.FetchPost(Cyphrme.API.Post.EmailCreate, formData)\n\t\tconsole.debug(\"Response: \", resp)\n\t\tCyphrme.Notification(\"Email was sent successfully. Check your inbox.\", \"success\")\n\t} catch (error) {\n\t\tCyphrme.Error(error)\n\t}\n}\n\n/**\nInitFromStorage inits login storage vars.\nDoes not init keys, or accounts because keys/accounts is for wallet.\n@returns {void}\n */\nasync function InitFromStorage() {\n\ttry {\n\t\tawait initSelected() // Init CozeKey, CozeKeyPublic from LS_SelectedKey\n\t\tawait initAccountFromStorage() // Init Account Details from LS_AccountDetails\n\t\t// Don't call InitKeys since that should only be for the wallet page.\n\t} catch (e) {\n\t\tCyphrme.Error(e) //throws error\n\t}\n\n\tif (isEmpty(UAD)) {\n\t\tconsole.debug(\"No account found for user.\")\n\t\treturn\n\t}\n\n\tif (isEmpty(AccountDetails)) {\n\t\tconsole.debug(\"No account details found for user.\")\n\t\treturn\n\t}\n\n\t//// GUI\n\n\t// Account Details\n\t// console.log(AccountDetails)\n\tlet ocl = document.getElementById(\"one_click_login\")\n\tlet oclImage = ' Welcome '\n\tif (!isEmpty(AccountDetails.profile_picture)) {\n\t\toclImage = ' Welcome '\n\t}\n\n\tocl.querySelector('span').innerHTML = oclImage + AccountDetails.display_name.substring(0, 15)\n\tocl.title = \"Go to your account page\"\n\tocl.href = \"/account\"\n}\n\n/**\ninitSelected gets the selected private Coze key from local storage and inits\nall related globals.\n@exception {SyntaxError} JSON parse exception. \n@returns {void}\n */\nasync function initSelected() {\n\tlet string = localStorage.getItem(LS_SelectedKey)\n\tif (isEmpty(string)) {\n\t\tconsole.debug('No selected key set.')\n\t\treturn\n\t}\n\tCozeKey = JSON.parse(string)\n\tCozeKeyExtra = CozeKey\n\tCozeKey.SerializedTmb = CozeKey.alg + \":\" + CozeKey.tmb\n\tCozeKeyPublic = JSON.parse(string)\n\tdelete CozeKeyPublic.d\n}\n\n/**\ninitAccountFromStorage gets Account Details from storage, sets UAD globally.\n@exception {SyntaxError} JSON parse exception.\n@returns {void}\n */\nasync function initAccountFromStorage() {\n\tAccountDetails = {}\n\tlet s = localStorage.getItem(LS_AccountDetails)\n\tif (!isEmpty(s)) {\n\t\tAccountDetails = JSON.parse(s)\n\t\t// If account has UAD, set it globally. \n\t\tif (AccountDetails && AccountDetails.uad) {\n\t\t\tUAD = AccountDetails.uad\n\t\t}\n\t}\n}\n\n/**\nInitAccounts gets accounts from storage and sets the global container variable.\n\nIf `Accounts` is already set, calling this function has no effect.\n@exception {SyntaxError} JSON parse exception.\n@returns {void}\n */\nasync function InitAccounts() {\n\tif (Object.keys(Accounts).length === 0) {\n\t\tlet stringedAccts = localStorage.getItem(LS_Accounts)\n\t\t// Accounts are empty if not in storage. Don't attempt JSON.parse if\n\t\t// empty since it overwrites Keys with undefined.\n\t\tif (!isEmpty(stringedAccts)) {\n\t\t\tAccounts = JSON.parse(stringedAccts)\n\t\t}\n\t}\n}\n\n/**\ninitKeys gets keys from storage and sets the global container variable. If\n`Keys` is already set, calling this function has no effect.\n@exception {SyntaxError} JSON parse exception.\n@returns {void}\n */\nasync function InitKeys() {\n\tif (Object.keys(Keys).length === 0) {\n\t\tlet stringedKeys = localStorage.getItem(LS_Keys)\n\t\t// Keys are empty if not in storage. Don't attempt JSON.parse if empty since\n\t\t// it overwrites Keys with undefined.\n\t\tif (!isEmpty(stringedKeys)) {\n\t\t\tKeys = JSON.parse(stringedKeys)\n\t\t}\n\t}\n}\n\n/**\nDeleteKey deletes the given key locally.\n@param {Tmb} id ID for the given key being deleted.\n@returns {void}\n */\nasync function DeleteKey(id) {\n\tdelete Keys[id]\n\tUpsertGlobalKeys()\n}\n\n/**\nDeleteLocalAccount deletes the given account locally. If the account is\nthe current logged in account, it is logged out.\n@param {UAD} uad id/uad for the given account being deleted.\n@returns {void}\n */\nasync function DeleteLocalAccount(uad) {\n\tdelete Accounts[uad]\n\tUpsertGlobalAccounts()\n\tif (uad === UAD) {\n\t\t// Does not redirect to home after logging out. If needing to redirect, this\n\t\t// func must accept another param 'noRedirect'.\n\t\tLogout(true)\n\t}\n}\n\n/**\nUpdateKid updates a kid for the given key locally.\n@param {Tmb} tmb ID for the key being updated.\n@param {string} newKid New `kid` for the key.\n@returns {boolean}\n */\nasync function UpdateKid(tmb, newKid) {\n\t// console.log(tmb, newKid)\n\t// console.debug(Keys)\n\tKeys[tmb].kid = newKid\n\t// console.debug(Keys[tmb])\n\tlocalStorage.setItem(LS_Keys, JSON.stringify(Keys))\n\treturn true\n}\n\n/**\nSetLocalAccount sets/updates the current local account from the given account\ndetails. The account is also set in the Accounts object.\n\nGlobal application UAD variable are set.\n\nDoes nothing when no given account details are given.\n@param {AccountDetails} account\n@returns {void}\n */\nasync function SetLocalAccount(account) {\n\tif (!UpdateAccount(account)) {\n\t\treturn\n\t}\n\n\t// Set current local account\n\tlocalStorage.setItem(LS_AccountDetails, JSON.stringify(account))\n\tAccountDetails = account\n\tUAD = account.uad\n}\n\n/**\nUpdateAccount sets/updates the given account. If the account is already set, the\naccount details are overwritten with the given account details. Errors when\nno given account details are given. Returns whether or not the account was\nupdated.\n@param {AccountDetails} account\n@returns {boolean}\n */\nasync function UpdateAccount(account) {\n\tif (account == \"\") {\n\t\treturn false // Do nothing\n\t}\n\tif (isEmpty(account)) {\n\t\tCyphrme.Error(\"No Account information set.\")\n\t\treturn false\n\t}\n\n\t// Set/update all local accounts\n\tAccounts[account.id] = account\n\tSetLocalAccounts(Accounts)\n\treturn true\n}\n\n\n/**\nUpdateLocalAccountDisplayName updates the given account's display name. Given\naccount must have 'id' and 'display_name' populated. If the given account does\nnot exist in 'Accounts' yet, it is created.\n\nErrors if account is empty, account.id is not a recognized Cyphrme hash, or if\naccount.display_name is empty.\n@param {Profile} account\n@returns {void}\n@throws {error}\n */\nasync function UpdateLocalAccountDisplayName(account) {\n\tif (isEmpty(account) || !Cyphrme.IsCyphrmeDigest(account.id) || isEmpty(account.display_name)) {\n\t\tCyphrme.Error(\"Given account does not meet the requirements for updating the display name.\")\n\t}\n\t// For local `AccountDetails` (user account profile).\n\tAccountDetails.display_name = account.display_name\n\tlocalStorage.setItem(LS_AccountDetails, JSON.stringify(AccountDetails))\n\t// For local `Accounts` (wallet).\n\tif (isEmpty(Accounts[account.id])) {\n\t\tAccounts[accounts.id] = {}\n\t}\n\tAccounts[account.id].display_name = account.display_name\n\tSetLocalAccounts(Accounts)\n}\n\n/**\nUpdate the given accounts with the display name. `accounts` should be in the\nfollowing form:\n{\n\"cLj8vsYtMBwYkzoFVZHBZo6SNL8wSdCIjCKAwXNuhOk\": \"z\",\n\"zxcLp3BEYYoAZxM9QlV7lS4o3Jn1T0dz9L0pWPZJnIs\": \"Jared\"\n}\n\nIf the account does not yet exist, it is created and set.\n@param {...Accounts} accounts One or many user's account details.\n@returns {void}\n */\nasync function UpdateAccountDisplayNames(accounts) {\n\tif (typeof accounts !== \"object\") {\n\t\tCyphrme.Error(\"Accounts must be passed in as an object.\")\n\t}\n\tfor (let id in accounts) {\n\t\t// console.debug(id)\n\t\tif (isEmpty(Accounts[id])) {\n\t\t\tAccounts[id] = {}\n\t\t}\n\t\tAccounts[id].display_name = accounts[id]\n\t}\n\tSetLocalAccounts(Accounts)\n}\n\n/**\nGetProfile returns an object with the updatable `AccountDetails` fields.\n@returns {Profile}\n */\nfunction GetProfile() {\n\tlet fields = [\"id\", \"display_name\", \"first_name\", \"last_name\", \"email\", \"address_1\", \"address_2\", \"phone_1\", \"phone_2\", \"city\", \"state\", \"zip\", \"country\"]\n\t/**@type {Profile} */\n\tlet p = {\n\t\t/** @protected */\n\t\tid: UAD\n\t}\n\t// Set Profile fields if they are not empty.\n\tfor (let f of fields) {\n\t\tif (f === \"id\") {\n\t\t\tcontinue\n\t\t}\n\t\tif (!Coze.isEmpty(AccountDetails[f])) {\n\t\t\tp[f] = AccountDetails[f]\n\t\t}\n\t}\n\treturn p\n}\n\n\n/**\nSets/updates the current local Accounts from the given accounts.\nErrors when the given accounts is empty.\n@param {object} accounts Accounts object with 'uad' as the key, and the account object as the value.\n@returns {void}\n */\nasync function SetLocalAccounts(accounts) {\n\tif (isEmpty(accounts)) {\n\t\tCyphrme.Error(\"No Accounts given.\")\n\t\treturn\n\t}\n\tAccounts = accounts\n\tUpsertGlobalAccounts()\n}\n\n/**\nSetSelectedKey sets the given private key to localStorage then calls init from\nstorage for that key.\n@param {PrivateCozeKey} cz\n@returns {boolean}\n */\nasync function SetSelectedKey(cz) {\n\t//console.debug(\"setSelectedKey\")\n\tif (isEmpty(cz)) {\n\t\tCyphrme.Error(\"Set Selected key: Key not set.\")\n\t\treturn\n\t}\n\t// console.log(\"Updating the app with Private CozeKey: \" + cz)\n\n\tswitch (typeof cz) { // Do this to avoid issues with escaping strings.\n\t\tcase \"object\":\n\t\t\tbreak\n\t\tdefault: // Default case is assumed to be a serialized string.\n\t\t\tcz = JSON.parse(cz)\n\t\t\tbreak\n\t}\n\n\tlocalStorage.setItem(LS_SelectedKey, JSON.stringify(cz))\n\tinitSelected() //Set Globals\n\tUpsertGlobalKey(cz)\n\treturn true\n}\n\n/**\nGlobalNewKey creates a new Coze Key with the given alg for the given account,\nand updates the application with the new key.\n@param {Alg} alg \n@returns {PrivateCozeKey}\n */\nasync function GlobalNewKey(alg) {\n\tlet cz = await Coze.NewKey(alg)\n\tUpsertGlobalKey(cz)\n\treturn cz\n}\n\n/**\nUpsertGlobalKey upserts a Coze Key to the `Keys` global and updates local\nStorage.\n@param {Key} cz\n@returns {void}\n */\nasync function UpsertGlobalKey(cz) {\n\t// console.debug(\"Adding Coze Key to Keys in Local Storage: \", cz)\n\tif (isEmpty(Keys)) {\n\t\tKeys = {}\n\t}\n\tKeys[cz.tmb] = cz\n\tUpsertGlobalKeys()\n}\n\n/**\nUpsertGlobalKeys sets the \"Keys\" object in local storage.\n@returns {void}\n */\nasync function UpsertGlobalKeys() {\n\t// console.debug(\"UpsertGlobalKeys\", Keys)\n\tlocalStorage.setItem(LS_Keys, JSON.stringify(Keys))\n}\n\n/**\nUpsertGlobalAccounts sets the \"Accounts\" object in local storage.\n@returns {void}\n */\nasync function UpsertGlobalAccounts() {\n\t// console.debug(\"UpsertGlobalAccounts\", Accounts)\n\tlocalStorage.setItem(LS_Accounts, JSON.stringify(Accounts))\n}\n\n\n/**\nVerifyAccess sends a request to the server to check whether or not the UAD\nhas been verified by Cyphr.me. \n@returns {void}\n */\nasync function VerifyAccess() {\n\tif (isEmpty(UAD)) {\n\t\tCyphrme.Notification(\"Account's UAD is not set\", 'error')\n\t\treturn\n\t}\n\tlet formData = new FormData()\n\tformData.append('accountID', UAD)\n\tAccountDetailsCallback(await Ajax.FetchPost(Cyphrme.API.Get.User, formData))\n}\n\n/**\nAccountDetailsCallback is the callback that accepts a response object which is\nassumed to contain the identify information of the person loading the page. \n\nIf the account is not verified by Cyphr.me, this function calls another function\nthat redirects them to an unverified account page. NOTE: That function should\nthen probably also store the response in some form, so that that user has\nlimited access to the application.\n@param {SuccessJSON} parsd\n@returns {void} \n */\nasync function AccountDetailsCallback(parsd) {\n\tif (isEmpty(parsd)) {\n\t\treturn\n\t}\n\tconsole.log(\"Setting local account details\")\n\tlocalStorage.setItem(LS_AccountDetails, JSON.stringify(parsd.obj))\n\tInitFromStorage()\n}\n\n/**\n!! WARNING !! \nClearLocalStorage clears all local storage for the Cyphr.me application.\nA User should have all of their keys backed up/downloaded before calling\nclearing local storage. Any checks to make sure that it is okay to clear\nlocal storage should be made before calling this function.\n@returns {void}\n */\nfunction ClearLocalStorage() {\n\tconsole.warn('Clear local storage called.')\n\tlocalStorage.clear()\n\tCyphrme.Notification('Successfully cleared local storage.', 'success')\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Coze funcs outside of the CozeJS lib\n////////////////////////////////////////////////////////////////////////////////\n\n/** TODO out of date\nReturns a normalized Coze key with \"alg\",\"iat\",\"tmb',\"x\", and\nif present, \"kid\", and \"y\".\n\nTruncates `kid` at 50 characters and throws on oversized `iat`.\n@param {Key} cozeKey\n@returns {Key}\n@throws {error} Throws if over JS max safe int.\n */\nasync function CozeKeyNormal(cozeKey) {\n\tvar nck = {}\n\tnck.alg = cozeKey.alg\n\tif (cozeKey.iat > 9007199254740991) { // max safe Javascript integer.\n\t\tthrow \"Coze.Normal: `iat` too large\"\n\t}\n\tnck.iat = cozeKey.iat\n\tif (!isEmpty(cozeKey.kid)) {\n\t\tnck.kid = cozeKey.kid.substring(0, 50) // `kid` soft limit of 50\n\t}\n\tnck.x = cozeKey.x\n\tif (Coze.Genus(cozeKey.alg) == \"ECDSA\") { // y is required for ECDSA\n\t\tnck.y = cozeKey.y\n\t}\n\tnck.tmb = cozeKey.tmb\n\n\treturn nck\n}", "\"use strict\";\n\nimport {\n\tIsImage,\n} from './file.js';\n\n\nexport {\n\tDragster,\n\tDragsterOnload, // Stuttering because app/dragster.js is namespaced to App not Dragster. \n\tDragsterSetCallback,\n\tDragsterSetProgressCallback,\n\tHandleFiles,\n}\n\n\n///////////////////\n// Dragster\n////////////////////\nfunction Dragster(el) {\n\tvar b = function(fn, me) {\n\t\treturn function() {\n\t\t\treturn fn.apply(me, arguments);\n\t\t};\n\t};\n\n\tthis.el = el;\n\tthis.dragenter = b(this.dragenter, this);\n\tthis.dragleave = b(this.dragleave, this);\n\tthis.first = false;\n\tthis.second = false;\n\tthis.el.addEventListener(\"dragenter\", this.dragenter, false);\n\tthis.el.addEventListener(\"dragleave\", this.dragleave, false);\n\tthis.el.addEventListener(\"drop\", this.dragleave, false);\n}\n\nDragster.prototype.dragenter = function(event) {\n\tif (this.first) {\n\t\treturn this.second = true;\n\t} else {\n\t\tthis.first = true;\n\t\tthis.customEvent = document.createEvent(\"CustomEvent\");\n\t\tthis.customEvent.initCustomEvent(\"dragster:enter\", true, true, {\n\t\t\tdataTransfer: event.dataTransfer,\n\t\t\tsourceEvent: event\n\t\t});\n\t\treturn this.el.dispatchEvent(this.customEvent);\n\t}\n};\n\nDragster.prototype.dragleave = function(event) {\n\tif (this.second) {\n\t\tthis.second = false;\n\t} else if (this.first) {\n\t\tthis.first = false;\n\t}\n\tif (!this.first && !this.second) {\n\t\tthis.customEvent = document.createEvent(\"CustomEvent\");\n\t\tthis.customEvent.initCustomEvent(\"dragster:leave\", true, true, {\n\t\t\tdataTransfer: event.dataTransfer,\n\t\t\tsourceEvent: event\n\t\t});\n\t\treturn this.el.dispatchEvent(this.customEvent);\n\t}\n};\n\nDragster.prototype.removeListeners = function() {\n\tthis.el.removeEventListener(\"dragenter\", this.dragenter, false);\n\tthis.el.removeEventListener(\"dragleave\", this.dragleave, false);\n\treturn this.el.removeEventListener(\"drop\", this.dragleave, false);\n};\n\nDragster.prototype.reset = function() {\n\tthis.first = false;\n\treturn this.second = false;\n};\n\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Helpful Information for importing/using this module //\n////////////////////////////////////////////////////////////////////////////////\n\n// To use this module, first set a callback:\n// E.g. Dragster.SetCallback(myCallbackFunc);\n// \n// A Page must have the following elements loaded for this module to work (as\n// expected):\n//\n// - file_drop_area\n// - fileUploadInput\n\n// Example of a File Upload section for a page.\n//\n//
\n//
\n// \t
\n// \t\t Upload Files\n// \t
\n// \t
\n// \t\t
\n// \t\t\t

Upload one or many files with the dialog or by dragging and dropping here

\n// \t\t\t\n// \t\t\t\n// \t\t\t
\n// \t\t\t\n// \t\t
\n// \t\t
\n// \t
\n//
\n//
\n\n// File Upload Vars\nlet dropzone = \"\"\nlet dragster\n// Name of preview gallery element ID\nlet gallery = \"upload_gallery\"\n\n/**\nDropFunction is called on a per file basis after drop. \n\nOptionally async, but must be async for progress to work. If progress is not\ndefined, async on the callback is not needed. \n@async\n@callback DropFunction Called after dragster drop for each file.\n@param {File} file\n*/\n\n\n/**\nProgressFunction is called on a per file basis with the current file number\nafter drop and DropFunction are complete. \n@callback ProgressFunction Called after dragster drop for each file.\n@param {number} i\n*/\n\n\n/**\ndragsterDropFunction is called with for each file from Files[] after dragster\ndrop. \n@type {DropFunction}\n*/\nvar dragsterDropFunction\n\n/**\ndragsterProgressFunction is called with for each file from Files[] after dragster\ndrop. \n@type {ProgressFunction}\n*/\nvar dragsterProgressFunction\n\n\n// Onload for loading page.\nasync function DragsterOnload() {\n\t// console.debug(\"Executing DragsterOnload\");\n\tdropzone = document.getElementById(\"file_drop_area\");\n\tdragster = new Dragster(dropzone);\n\n\tdocument.addEventListener(\"dragster:enter\", (e) => {\n\t\te.target.classList.add(\"highlight\");\n\t}, false);\n\tdocument.addEventListener(\"dragster:leave\", (e) => {\n\t\te.target.classList.remove(\"highlight\");\n\t}, false);\n\n\t// Handle dropped files\n\tdropzone.addEventListener('drop', handleDrop, false);\n\n\t// Prevent default drag behaviors.\n\t// For unknown reasons dragover is yanking the drop event it's not stopped.\n\t// dragenter and dragleave are just for extra measure. \n\t['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {\n\t\t//dropzone.addEventListener(eventName, preventDefaults, false);\n\t\tdocument.body.addEventListener(eventName, preventDefaults, false);\n\t});\n\n\t// Trigger the actual input from the GUI input.\n\tdocument.querySelector(\"#file_drop_area .fileUploadBtn\").addEventListener('click', (event) => {\n\t\tevent.preventDefault();\n\t\tdocument.querySelector(\"#file_drop_area .fileUploadInput\").click();\n\t});\n\t// Trigger the file upload event from the GUI File Drop Section.\n\tdocument.querySelector(\"#file_drop_area .fileUploadInput\").onchange = (event) => HandleFiles(event.target.files)\n};\n\n\n/**\nSetUploadCallbacks sets the global variable for which callback\nfunction to use for the drag and drop upload.\n@param {DropFunction} callback Called after dragster drop for each file.\n@returns {void}\n */\nfunction DragsterSetCallback(callback) {\n\tdragsterDropFunction = callback;\n};\n\n\n/**\nSetUploadCallbacks sets the global variable for which callback\nfunction to use for the drag and drop upload.\n@param {DropFunction} callback Called after dragster drop for each file.\n@returns {void}\n */\nfunction DragsterSetProgressCallback(callback) {\n\tdragsterProgressFunction = callback;\n};\n\n\n\n/**\npreventDefaults prevents default actions from happening if the event is not\nexplicitly handled. It also prevents propagation from occurring.\n@param {Event} e e is the event that is occurring.\n@returns {void}\n */\nfunction preventDefaults(e) {\n\te.preventDefault();\n\te.stopPropagation();\n};\n\n/**\nhandleDrop gets files from the data transfer that happened on the event passed\nin. It then passes the files to the helper function: HandleFiles.\n@param {Event} e an event that occurred for transferring some type of files.\n@returns {void}\n */\nfunction handleDrop(e) {\n\tvar dt = e.dataTransfer;\n\tvar files = dt.files;\n\n\tHandleFiles(files);\n};\n\n\n/**\nHandleFiles accepts variadic files handled. For each file it calls previewFile,\nwhich displays the files to the page, and the dragster callback. \n@param {Blob[]} files Array of image file bytes.\n@returns {void} \n */\nasync function HandleFiles(files) {\n\tconsole.log(\"HandleFiles\", files);\n\tif (files instanceof DataTransferItemList) {\n\t\tlet blobs = [];\n\t\tfor (var index in files) {\n\t\t\tvar item = files[index];\n\t\t\tif (item.kind === 'file') {\n\t\t\t\tvar blob = item.getAsFile();\n\t\t\t\tvar reader = new FileReader();\n\t\t\t\treader.onload = function(event) {\n\t\t\t\t\tconsole.log(event.target.result); // data url\n\t\t\t\t};\n\t\t\t\treader.readAsDataURL(blob);\n\t\t\t\tblobs.push(blob)\n\t\t\t}\n\t\t}\n\t\tfiles = blobs;\n\t}\n\tfiles = [...files]; // TODO Why?\n\n\tfor (let i = 0; i < files.length; i++) {\n\t\tpreviewFile(files[i])\n\t\tawait dragsterDropFunction(files[i])\n\t\tif (typeof ProgressFunction === \"function\") { // ProgressFunction is Optional \n\t\t\tProgressFunction(i)\n\t\t}\n\t}\n};\n\n\n\n/**\npreviewFile accepts a file, and reads it as a URL (blob).\nAfter the raw file is read, it is then displayed on the page in the\nimage gallery.\n@param {Blob} file Raw file bytes being read.\n@returns {void}\n */\nfunction previewFile(file) {\n\t// console.log(\"previewFile\");\n\n\tif (IsImage(file.name)) {\n\t\tlet reader = new FileReader();\n\t\treader.readAsDataURL(file);\n\t\treader.onloadend = function() {\n\t\t\tvar item = document.createElement('img');\n\t\t\titem.src = reader.result;\n\t\t\tdocument.getElementById(gallery).appendChild(item);\n\t\t};\n\t\treturn;\n\t}\n\t// console.log(\"Not an image\");\n\tvar item = document.createElement('i');\n\titem.setAttribute(\"class\", \"bi bi-file-earmark-text icon display-1\");\n\tdocument.getElementById(gallery).appendChild(item);\n}\n\n\n\n\n///////////////////////////////\n// Original CoffeeScript\n// DO NOT DELETE WITHOUT TALKING TO ZAMI. \n///////////////////////////////\n// Original Coffeescript output:\n//https://github.com/bensmithett/dragster\n// Zami personally looked over the lib. \n// Generated by CoffeeScript 1.12.7\n// (function() {\n// \tvar Dragster,\n// \t\tbind = function(fn, me) {\n// \t\t\treturn function() {\n// \t\t\t\treturn fn.apply(me, arguments);\n// \t\t\t};\n// \t\t};\n\n// \tDragster = (function() {\n// \t\tfunction Dragster(el) {\n// \t\t\tthis.el = el;\n// \t\t\tthis.dragleave = bind(this.dragleave, this);\n// \t\t\tthis.dragenter = bind(this.dragenter, this);\n// \t\t\tthis.first = false;\n// \t\t\tthis.second = false;\n// \t\t\tthis.el.addEventListener(\"dragenter\", this.dragenter, false);\n// \t\t\tthis.el.addEventListener(\"dragleave\", this.dragleave, false);\n// \t\t\tthis.el.addEventListener(\"drop\", this.dragleave, false);\n// \t\t}\n\n// \t\tDragster.prototype.dragenter = function(event) {\n// \t\t\tif (this.first) {\n// \t\t\t\treturn this.second = true;\n// \t\t\t} else {\n// \t\t\t\tthis.first = true;\n// \t\t\t\tthis.customEvent = document.createEvent(\"CustomEvent\");\n// \t\t\t\tthis.customEvent.initCustomEvent(\"dragster:enter\", true, true, {\n// \t\t\t\t\tdataTransfer: event.dataTransfer,\n// \t\t\t\t\tsourceEvent: event\n// \t\t\t\t});\n// \t\t\t\treturn this.el.dispatchEvent(this.customEvent);\n// \t\t\t}\n// \t\t};\n\n// \t\tDragster.prototype.dragleave = function(event) {\n// \t\t\tif (this.second) {\n// \t\t\t\tthis.second = false;\n// \t\t\t} else if (this.first) {\n// \t\t\t\tthis.first = false;\n// \t\t\t}\n// \t\t\tif (!this.first && !this.second) {\n// \t\t\t\tthis.customEvent = document.createEvent(\"CustomEvent\");\n// \t\t\t\tthis.customEvent.initCustomEvent(\"dragster:leave\", true, true, {\n// \t\t\t\t\tdataTransfer: event.dataTransfer,\n// \t\t\t\t\tsourceEvent: event\n// \t\t\t\t});\n// \t\t\t\treturn this.el.dispatchEvent(this.customEvent);\n// \t\t\t}\n// \t\t};\n\n// \t\tDragster.prototype.removeListeners = function() {\n// \t\t\tthis.el.removeEventListener(\"dragenter\", this.dragenter, false);\n// \t\t\tthis.el.removeEventListener(\"dragleave\", this.dragleave, false);\n// \t\t\treturn this.el.removeEventListener(\"drop\", this.dragleave, false);\n// \t\t};\n\n// \t\tDragster.prototype.reset = function() {\n// \t\t\tthis.first = false;\n// \t\t\treturn this.second = false;\n// \t\t};\n\n// \t\treturn Dragster;\n\n// \t})();\n\n// \tif (typeof module === 'undefined') {\n// \t\twindow.Dragster = Dragster;\n// \t} else {\n// \t\tmodule.exports = Dragster;\n// \t}\n\n// }).call(this);\n\n\n///////////////////////////////\n// Modified CoffeeScript to kill module exports\n// DO NOT DELETE WITHOUT TALKING TO ZAMI. \n///////////////////////////////\n//https://github.com/bensmithett/dragster\n// Zami personally looked over the lib. \n// var Dragster = (function() {\n\n\n// \tvar b = function(fn, me) {\n// \t\treturn function() {\n// \t\t\treturn fn.apply(me, arguments);\n// \t\t};\n// \t};\n\n// \tfunction D(el) {\n// \t\tthis.el = el;\n// \t\tthis.dragleave = b(this.dragleave, this);\n// \t\tthis.dragenter = b(this.dragenter, this);\n// \t\tthis.first = false;\n// \t\tthis.second = false;\n// \t\tthis.el.addEventListener(\"dragenter\", this.dragenter, false);\n// \t\tthis.el.addEventListener(\"dragleave\", this.dragleave, false);\n// \t\tthis.el.addEventListener(\"drop\", this.dragleave, false);\n// \t}\n\n// \tD.prototype.dragenter = function(event) {\n// \t\tif (this.first) {\n// \t\t\treturn this.second = true;\n// \t\t} else {\n// \t\t\tthis.first = true;\n// \t\t\tthis.customEvent = document.createEvent(\"CustomEvent\");\n// \t\t\tthis.customEvent.initCustomEvent(\"dragster:enter\", true, true, {\n// \t\t\t\tdataTransfer: event.dataTransfer,\n// \t\t\t\tsourceEvent: event\n// \t\t\t});\n// \t\t\treturn this.el.dispatchEvent(this.customEvent);\n// \t\t}\n// \t};\n\n// \tD.prototype.dragleave = function(event) {\n// \t\tif (this.second) {\n// \t\t\tthis.second = false;\n// \t\t} else if (this.first) {\n// \t\t\tthis.first = false;\n// \t\t}\n// \t\tif (!this.first && !this.second) {\n// \t\t\tthis.customEvent = document.createEvent(\"CustomEvent\");\n// \t\t\tthis.customEvent.initCustomEvent(\"dragster:leave\", true, true, {\n// \t\t\t\tdataTransfer: event.dataTransfer,\n// \t\t\t\tsourceEvent: event\n// \t\t\t});\n// \t\t\treturn this.el.dispatchEvent(this.customEvent);\n// \t\t}\n// \t};\n\n// \tD.prototype.removeListeners = function() {\n// \t\tthis.el.removeEventListener(\"dragenter\", this.dragenter, false);\n// \t\tthis.el.removeEventListener(\"dragleave\", this.dragleave, false);\n// \t\treturn this.el.removeEventListener(\"drop\", this.dragleave, false);\n// \t};\n\n// \tD.prototype.reset = function() {\n// \t\tthis.first = false;\n// \t\treturn this.second = false;\n// \t};\n\n// \treturn D;\n\n// })();", "\"use strict\";\n\n// Application imports\nimport * as Cyphrme from './cyphrme.js';\nimport * as Login from './login.js';\nimport * as Ajax from './ajax.js';\nimport * as CVA from './cva.js';\nimport * as Dragster from './dragster.js';\n\nexport {\n\t// Images\n\tImageOnload,\n\tDisplayImages,\n\n\t// Files\n\tFileOnload,\n\tDisplayFiles,\n\tUploadFile,\n\n\t// Both\n\tDeleteFile,\n\tDownloadTxt,\n\tGetFileExtension,\n\tIsImage,\n};\n\n/**\n@typedef {import('../page/image.js').ImagePage} ImagePage\n@typedef {import('../../../pkg/cozejs/typedef.js').B64} B64\n*/\n\n/**\nA File is an image or file object, that should mirror the `FileStore` model in\nthe server, with any additional fields/data needed for the GUI.\n\n- id: The B64 ID of the image (from the czd).\n- uad: The B64 ID of the user that owns the image.\n- ext: The extension of the image.\n- dig: The digest of the image.\n- order: The order of precedence for the image, respective to the other images\n on the page.\n- parent: The parentID that the image is attached to.\n- root: The root/core subject matter that the image is on.\n- time: The time the image was inserted into the database.\n- updated: The time the image was last updated.\n- uad: The UAD of the image.\n- utk: The UTK time index for the image.\n- ht: The HT (Hash Time).\n- deleted: Whether the record has been marked deleted in the database.\n- is_image: Whether or not the file is an image.\n@typedef {object} File\n@property {B64} id\n@property {B64} uad\n@property {string} ext\n@property {B64} dig\n@property {number} [order]\n@property {B64} root\n@property {B64} [parent]\n@property {number} time\n@property {number} updated\n@property {B64} uad\n@property {B64} utk\n@property {HT} ht\n@property {boolean} [deleted]\n@property {boolean} is_image\n */\n\n/**\n@typedef {object.} ImageCzdObj - Holds images on the page.\n */\n\n/**\n@typedef {object.} FileCzdObj - Holds files on the page.\n */\n\n\n/////////////////\n// Variables\n////////////////\nvar DragsterLoaded = false;\n\n/** @type {ImagePage} ImagePage */\nvar ImagePage\nvar CurrentImageDig;\nvar CurrentImageID;\nvar CurrentImageRoot;\nvar ImageLimit = 20; // Page limit for images. For full screen, should be 1 row of 4.\nvar imageModal; // The Bootstrap modal instance.\nvar Ime; // The Image Modal HTML Element. \nvar ImageCzdObj = {}; // Holds all of the images on the page.\n\n/** @type {FilePage} FilePage */\nvar FilePage;\nvar FileCzdObj = {}; // Holds all of the files on the page.\nvar FileLimit = 20; // Page limit for files. For full screen, should be 1 row of 3.\n\n/**\nImageOnLoad initializes the javascript for uploading and performing other\nimage actions on the page.\n// TODO better naming or explanation why ImageOnload is different from FileOnload\n@param {ImagePage} imagePage\n@returns {void}\n */\nasync function ImageOnload(imagePage) {\n\tconsole.debug(\"Executing ImageOnload\")\n\tif (!DragsterLoaded) {\n\t\tawait Dragster.DragsterSetCallback(uploadImage)\n\t\tDragster.DragsterOnload()\n\t\tDragsterLoaded = true\n\t}\n\tif (isEmpty(imagePage)) {\n\t\tconsole.log(\"ImagePage is empty.\")\n\t\treturn\n\t}\n\tDisplayImages(imagePage)\n}\n\n/**\nFileOnload initializes the javascript for uploading, deleting, and displaying\nfiles on the page.\n// TODO better naming or explanation why ImageOnload is different from FileOnload\n@param {FilePage} filePage\n@returns {void}\n */\nasync function FileOnload(filePage) {\n\tconsole.debug(\"Executing FileOnload\");\n\tif (!DragsterLoaded) {\n\t\tawait Dragster.DragsterSetCallback(uploadImage);\n\t\tDragster.DragsterOnload();\n\t\tDragsterLoaded = true;\n\t}\n\n\t// Set event listeners in case files are added later.\n\t// Delete files button.\n\tvar delBtn = document.querySelector(\".acFilesDeleteBtn\")\n\tif (delBtn !== null) {\n\t\tdelBtn.addEventListener('click', () => {\n\t\t\tfor (let l of document.querySelectorAll('#cy_file_gallery li')) {\n\t\t\t\tif (l.querySelector('input').checked) {\n\t\t\t\t\tDeleteFile(l.id);\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n\n\tif (isEmpty(filePage)) {\n\t\tconsole.log(\"FilePage is empty.\");\n\t\treturn;\n\t}\n\tFilePage = filePage;\n\tDisplayFiles();\n};\n\n/**\nDisplayImages displays all of the images on a given page. This function should\nbe called in the on load function. This checks to see if ImagePage.images is\nempty, and if not, it displays them on the page.\n@param {ImagePage} imagePage\n@returns {void}\n */\nfunction DisplayImages(imagePage) {\n\t// Set global here, and not onload. For some reason, the global shows\n\t// undefined when set in onload, and when passed, this function recognizes it.\n\t// The file onload sets FilePage in the onload, and it works as expected.\n\t// WTF?\n\tImagePage = imagePage\n\tif (isEmpty(ImagePage.images)) {\n\t\tconsole.log(\"ImagePage does not have any images.\");\n\t\treturn;\n\t}\n\n\t// WTF\n\tif (!isEmpty(ImagePage.paginate.limit)) {\n\t\tImageLimit = ImagePage.paginate.limit;\n\t}\n\n\t// \"Show more images\". Applies to modal and everything else. \n\tif (!isEmpty(ImagePage.has_more)) {\n\t\tlet btns = document.querySelectorAll('.ShowMoreImagesBtn');\n\t\tconsole.log(btns);\n\t\tfor (let btn of btns) {\n\t\t\tconsole.log(btn)\n\t\t\tShow(btn);\n\t\t\tconsole.log(\"Link\", Cyphrme.Page.Images + \"/\" + ImagePage.images[0].id + \"?limit=20\");\n\t\t\tbtn.href = (Cyphrme.Page.Images + \"/\" + ImagePage.images[0].id + \"?limit=20\");\n\t\t}\n\t\t// let btn = Ime.querySelector('#ShowMoreImagesBtn');\n\n\t}\n\n\n\tIme = document.getElementById('imageModal');\n\timageModal = new bootstrap.Modal(Ime, {\n\t\tkeyboard: true\n\t}); // imageModal is the Bootstrap instance, not html element. \n\n\t// Set click navigation on arrows on modal.\n\tIme.querySelector('.modalArrowLeft').addEventListener('click', function () {\n\t\tsetModalArrows(true);\n\t});\n\tIme.querySelector('.modalArrowRight').addEventListener('click', function () {\n\t\tsetModalArrows(false);\n\t});\n\n\t// Set keyboard navigation on modal. \n\tvar keyArrows = function (event) {\n\t\tif (event.keyCode == 37) {\n\t\t\tsetModalArrows(true);\n\t\t}\n\t\tif (event.keyCode == 39) {\n\t\t\tsetModalArrows(false);\n\t\t}\n\t};\n\n\tIme.addEventListener('show.bs.modal', function (e) {\n\t\tdocument.addEventListener('keydown', keyArrows);\n\t});\n\tIme.addEventListener('hide.bs.modal', function (e) {\n\t\tdocument.removeEventListener('keydown', keyArrows);\n\t});\n\n\tIme.querySelector('.modal_delete_image').addEventListener('click', () => DeleteFile());\n\n\n\t// For image gallery\n\tfor (let i in ImagePage.images) {\n\t\tImageCzdObj[ImagePage.images[i].id] = ImagePage.images[i]; // Set ImageCzdObj\n\n\t\tif (i == ImageLimit) {\n\t\t\tbreak;\n\t\t}\n\t\tappendTmbImageToGallery(ImagePage.images[i]);\n\t}\n}\n\n/**\nDisplayFiles displays all of the files (non images) on a given page. This\nfunction should be called in the on load function. This checks to see if\nFilePage.files is empty, and if not, it displays them on the page, while also\nsetting event listeners.\n@returns {void}\n */\nasync function DisplayFiles() {\n\t// console.debug(\"Display Files\");\n\n\tif (isEmpty(FilePage.files)) {\n\t\tconsole.log(\"FilePage does not have any files.\");\n\t\treturn;\n\t}\n\n\t// WTF\n\tif (!isEmpty(FilePage.paginate.limit)) {\n\t\tFileLimit = FilePage.paginate.limit;\n\t}\n\n\t// For file gallery\n\tlet colCounter = 0;\n\tlet cols = document.querySelectorAll(\"#cy_file_gallery .col\");\n\tlet fileCounter = 0;\n\tfor (let i in FilePage.files) {\n\t\tFileCzdObj[FilePage.files[i].id] = FilePage.files[i]; // Set FileCzdObj\n\n\t\tif (i == FileLimit) {\n\t\t\tbreak;\n\t\t}\n\n\t\tappendTmbFileToGallery(FilePage.files[i], cols[colCounter]);\n\t\t// For placing files in alternating columns.\n\t\tif (colCounter == cols.length - 1) {\n\t\t\tcolCounter = 0;\n\t\t} else {\n\t\t\tcolCounter++;\n\t\t}\n\t\tfileCounter++;\n\t}\n\t// Set GUI based on whether or not user on page is the owner.\n\tif (fileCounter > 0) {\n\t\tif (FilePage.uad === Login.UAD) {\n\t\t\tShow(document.querySelector(\".acFilesDeleteBtn\"));\n\t\t} else {\n\t\t\tfor (let div of document.querySelectorAll('#cy_file_gallery .form-check-input')) {\n\t\t\t\tdiv.parentElement.classList.remove('form-check');\n\t\t\t\tHide(div);\n\t\t\t}\n\t\t}\n\t\tShow(\"cy_file_gallery\");\n\t}\n}\n\n/**\nAppends the file thumbprint the the GUI file gallery.\n@param {File} file\n@param {HTMLElement} listDiv HTML ul element for appending file to.\n@returns {Void}\n */\nasync function appendTmbFileToGallery(file, listDiv) {\n\t// console.debug(\"File: \", file, \"Div: \", listDiv);\n\tlet li = document.createElement('li');\n\tlet a = document.createElement('a');\n\ta.target = \"_blank\"; // Open in new tab.\n\ta.rel = \"noopener noreferrer\"; // Open in new tab.\n\ta.href = Cyphrme.API.Get.File + \"/\" + file.dig;\n\ta.textContent = file.file_name;\n\tli.id = file.id;\n\tlet check = document.createElement('div');\n\tcheck.classList.add('form-check');\n\tlet input = document.createElement('input');\n\tinput.classList.add('form-check-input');\n\tinput.type = \"checkbox\";\n\n\tcheck.append(input);\n\tcheck.append(a);\n\tli.append(check)\n\tlistDiv.querySelector('ul').append(li);\n\tShow(\"cy_file_gallery\"); // Always show, in case it is hidden.\n}\n\n/**\nAppends the image thumbprint to the GUI image gallery.\n@param {Image} image\n@returns {Void}\n */\nfunction appendTmbImageToGallery(image) {\n\tlet imageDiv = document.getElementById('image_template').content.cloneNode(true);\n\tlet img = document.createElement('img');\n\tlet src = Cyphrme.API.Get.Image + '/' + image.dig;\n\timg.id = image.id;\n\n\tif (image.ext === \"svg\") { // svg's don't have thumbnails. \n\t\tsrc += '.svg';\n\t\timg.dataset.imagedig = image.dig;\n\t} else {\n\t\tsrc += '-TN1';\n\t\timg.dataset.imagedig = image.dig + '-TN1';\n\t}\n\timg.src = src;\n\timg.classList.add('cyphrImg', 'imgThumb');\n\timg.addEventListener('click', function () {\n\t\tsetImageModal(img.id);\n\t\timageModal.toggle();\n\t});\n\n\timageDiv.querySelector('.image_container').append(img);\n\tdocument.querySelector('#cy_image_gallary .row').append(imageDiv);\n}\n\n/**\nsetImageModal accepts an id, and the img Element being modified.\nThis function modifies the Modal/element for the new id/image passed in.\n@param {B64} czd - czd of the new image.\n@returns {void}\n */\nfunction setImageModal(czd) {\n\tCurrentImageDig = ImageCzdObj[czd].dig;\n\tCurrentImageID = czd;\n\tCurrentImageRoot = ImageCzdObj[czd].root;\n\n\tvar img = document.getElementById('imageModalImage');\n\timg.dataset.imageid = czd;\n\timg.dataset.imagedig = ImageCzdObj[czd].dig;\n\timg.setAttribute('src', Cyphrme.API.Get.Image + '/' + ImageCzdObj[czd].dig);\n\n\n\tIme.querySelector('.modal_delete_image').hidden = !(Login.UAD === ImageCzdObj[czd].uad);\n\tIme.querySelector('.modal_image_action').href = \"/e/\" + CurrentImageID;\n\tIme.querySelector('.modal_image_root').href = \"/s/\" + CurrentImageRoot;\n}\n\n/**\nsetModalArrows determines the index of the current image in the modal, relative\nto the order in which they are loaded and displayed on the page. Then, depending\non the param passed in (true or false), the modal is modified accordingly.\nPassing True to this function is assuming the left button was clicked and the\nprevious image is loaded. If clicking previous and the image is the first image\nin the list, the last image is pulled up. Passing False to this function assumes\nthe right button was clicked.\n\n@param {boolean} left True if left button, and false, if right.\n@returns {void}\n */\nfunction setModalArrows(left) {\n\tlet czdKeys = Object.keys(ImageCzdObj);\n\tif (czdKeys.length == 1) { // If there's only one, return.\n\t\treturn;\n\t}\n\n\tlet ind = czdKeys.indexOf(CurrentImageID);\n\tlet l = ind;\n\tlet r = ind;\n\n\tif (czdKeys.length > ImageLimit) {\n\t\tczdKeys = czdKeys.slice(0, ImageLimit);\n\t}\n\tif (ind === 0) {\n\t\tl = czdKeys.length;\n\t} else if (ind === (czdKeys.length - 1)) {\n\t\tr = -1;\n\t}\n\t//// Debugging\n\t// console.debug(l);\n\t// console.debug(r);\n\t// console.debug(czdKeys.length);\n\n\t// Prevents infinite loop, if the modal still somehow loaded without any images\n\t// left on the page. Sanity check to prevent bugs.\n\tif (czdKeys.length == 0) {\n\t\treturn;\n\t}\n\n\t// Loop is to account for multiple images that may be removed from the page.\n\t// The loop continues until if finds the next image.\n\tif (left) { // Left arrow\n\t\twhile (true) {\n\t\t\tif (isEmpty(czdKeys[l - 1])) {\n\t\t\t\tl--;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tsetImageModal(czdKeys[l - 1]);\n\t} else { // Right arrow\n\t\twhile (true) {\n\t\t\tif (isEmpty(czdKeys[r + 1])) {\n\t\t\t\tr++;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tsetImageModal(czdKeys[r + 1]);\n\t}\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// File Helpers\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nGetFileExtension accepts a file.name and return the extension, as a string.\n@param {string} filename File name\n@returns {string} ext File extension\n */\nfunction GetFileExtension(filename) {\n\tlet parts = filename.split('.')\n\tif (parts.length < 2) {\n\t\tthrow new Error(\"helpers.GetFileExtension: file does not have extension\")\n\t}\n\treturn parts[parts.length - 1]\n}\n\n/**\nIsImage accepts a file and return the extension, as a string.\n@param {string} filename File name\n@returns {boolean} If file is an image\n */\nfunction IsImage(fileName) {\n\treturn [\"jpeg\", \"jpg\", \"png\", \"gif\"].includes(GetFileExtension(fileName))\n}\n\n/**\nThis function accepts data to be written to a file, and the filename for\nthe file. Writes to the file, and downloads with the given name.\n@param {string} txt Data being written to the file.\n@param {string} filename Filename for file being downloaded.\n@returns {void}\n */\nfunction DownloadTxt(txt, filename) {\n\tlet downloadLink = document.createElement(\"a\") // Download link\n\tdownloadLink.download = filename // File name\n\tdownloadLink.href = window.URL.createObjectURL(new Blob([txt], {\n\t\ttype: \"text/txt\"\n\t})) // Create a link to the file\n\tdownloadLink.style.display = \"none\" // Make sure that the link is not displayed\n\tdocument.body.appendChild(downloadLink) // Add link to DOM\n\tdownloadLink.click() // Simulate a click.\n}\n\n/**\ngetFileFromPath returns a file blob from a given path.\n@param {string} path path to file (including relative)\n@returns {Blob}\n */\nasync function getFileFromPath(path) {\n\tlet response = await fetch(path)\n\treturn response.blob()\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// AJAX Section\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nUploadFile creates an upload coze and uploads file data and coze. File may be\ngeneric file or image file. \n// TODO rename parsd to rjson\n@param {File} file file is the file being uploaded.\n@returns {{coze,parsd}} // File creation coze and the parsed response from the server.\n@throws\n */\nasync function UploadFile(file) {\n\tif (isEmpty(Login.UAD)) {\n\t\tCyphrme.Error(\"error: must be logged in to upload images\")\n\t\treturn\n\t}\n\tif (file.size > Cyphrme.MaxFileSize) {\n\t\tCyphrme.Error(\"error: The file is too large. Must be under 30 mb.\")\n\t}\n\n\ttry {\n\t\tconsole.log(\"UploadFile\", file, ImagePage);\n\t\tif (isEmpty(ImagePage)) {\n\t\t\tvar coze = await CVA.FileCreate(file)\n\t\t} else {\n\t\t\tvar coze = await CVA.FileCreate(file, ImagePage.id, ImagePage.child_ac)\n\t\t}\n\t\tlet formData = new FormData()\n\t\tformData.append('file', file)\n\t\tformData.append('coze', JSON.stringify(coze))\n\t\tvar parsd = await Ajax.FetchPost(Cyphrme.API.Post.File, formData)\n\t} catch (e) {\n\t\tconsole.error(e)\n\t}\n\treturn {\n\t\tcoze: coze,\n\t\tparsd: parsd\n\t}\n}\n\n\n/**\nuploadImage is the callback function for image uploading. Adds the\nimages to the page on a successful upload, and show a notification message with\nthe appropriate error message, if the image upload is not successful.\n@param {File} file file is the file being uploaded.\n@returns {void}\n */\nasync function uploadImage(file) {\n\tlet cp = await UploadFile(file)\n\n\t//console.debug(\"uploadImage cp: \", cp)\n\tlet image = cp.parsd.obj\n\tImageCzdObj[cp.coze.czd] = {\n\t\t\"id\": cp.coze.czd,\n\t\t\"dig\": cp.coze.pay.id\n\t}\n\n\t// Files\n\tif (!image.is_image) {\n\t\tappendTmbFileToGallery(image, document.querySelector(\"#cy_file_gallery .col\"))\n\t\tdocument.getElementById(image.id).scrollIntoView()\n\t\tShow(document.querySelector('.acFilesDeleteBtn')) // Always show, since only owners can upload files.\n\t\tCyphrme.Notification(\"Successfully uploaded file!\", 'success')\n\t\treturn\n\t}\n\n\t// Images\n\tdocument.getElementById('file_drop_area').querySelector('.card').classList.add('border-success')\n\tappendTmbImageToGallery(image)\n\tdocument.getElementById(image.id).scrollIntoView()\n\tCyphrme.Notification(\"Successfully uploaded image!\", 'success')\n}\n\n\n/**\nDeleteFile sends a form to the server to delete the file with the\nassociated ID/Czd.\n\nIf czd isn't populated, uses \"Current\"\n@param {B64} [czd] Czd of the file.\n@returns {void}\n */\nasync function DeleteFile(czd) {\n\tif (isEmpty(czd)) {\n\t\tczd = CurrentImageID;\n\t}\n\tif (isEmpty(Login.UAD)) {\n\t\tCyphrme.Error('Account ID not set.');\n\t}\n\n\tlet formData = new FormData();\n\tformData.append('cozes', \"[\" + JSON.stringify(await CVA.FileDelete(czd)) + \"]\");\n\tlet parsd = await Ajax.FetchPost(Cyphrme.API.Post.ImageDelete, formData)\n\t// console.debug(\"Parsd: \", parsd)\n\tif (isEmpty(parsd)) {\n\t\treturn\n\t}\n\n\tdocument.getElementById(parsd.obj).remove()\n\n\tif (parsd.obj in ImageCzdObj) {\n\t\tdelete ImageCzdObj[parsd.obj] // Delete from ImageCzdObj\n\t}\n\tif (parsd.obj in FileCzdObj) {\n\t\tdelete FileCzdObj[parsd.obj] // Delete from FileCzdObj\n\t}\n\tif (document.querySelectorAll('#cy_file_gallery li').length <= 0) {\n\t\tHide(\"cy_file_gallery\") // TODO kill cy\n\t}\n\tCyphrme.Notification(\"Successfully deleted image.\", 'success')\n}\n\n//// Deprecated. Supported images supported by the GO server, and determines\n// whether or not to display as a file, or an image.\n// /**\n// * uploadImage uploads an image for supported image file types.\n// * \n// * @param {File} file Image being uploaded.\n// * @param {number} i i number for the upload's progress bar.\n// * @returns {void}\n// * @throws {error} Fails when file is not a supported image.\n// */\n// async function uploadImage(file, i) {\n// \tlet ext = GetFileExtension(file.name);\n// \t// TODO svg's are not displaying in html for unknown reason. If that works,\n// \t// support svg. Everything else with svg tested.\n// \tif ([\"jpeg\", \"jpg\", \"png\", \"gif\"].includes(ext)) {\n// \t\tUploadFile(file, i);\n// \t\treturn;\n// \t}\n// \tCyphrme.Error(\"unsupported image type \" + ext);\n// };", "\"use strict\"\n\n// This module currently only handles what is referred to throughout the app as\n// the `AC Attributes Options Form`, and not the `AC Print Options Form`.\n// ac_gen.js handles the print options form.\n// Until URLFormJS can handle multiple forms on the page, this module handles\n// blacklisting the form parameters from the print options form, as to not allow\n// any of the printing options in the signed payload.\n\n//// How to add more pre-defined options to the options form:\n// - Add the option in the `ac_options.tmpl`.\n// - Add the option to the URLFormJS FormParameters.\n// - Add check in `ACRegenPay`.\n// - Add check in `ProcessOpts`.\n// - Add check in `specialReserved` in `ac.js`.\n// - Add field to AC in Go.\n\nimport * as Cyphrme from './cyphrme.js'\nimport * as Login from './login.js'\nimport * as CVA from './cva.js'\nimport * as Ajax from './ajax.js'\n\nimport * as Coze from '../pkg/coze_all~fv=-7uCIHNa.min.js'\nimport * as Lib from '../lib/lib~fv=LNbMt1n5.min.js'\nimport '../pkg/urlform~fv=VhWyOSvq.min.js' // Namespaced as 'URLForm'.\n\nexport {\n\tACFormOptions,\n\tAcsFromTree,\n\tACGetOptions,\n\tACGetOptionsExtra,\n\tACGetBlacklistedPrintOptions, // TODO deprecate\n\tACRegenPay,\n\tInitACOptions,\n\tCheckModelParent,\n\tInitSubmitOptions,\n\tSetOriginalAC,\n\tProcessOpts,\n\tRecalcOptionGUI,\n\tGetStickerTitle,\n}\n\n/**\n@typedef {import('../../../pkg/cozejs/typedef.js').B64} B64\n@typedef {import('../../../pkg/cozejs/typedef.js').Pay} Pay\n@typedef {import('../../../pkg/cozejs/typedef.js').Can} Can\n@typedef {import('../../../pkg/urlformjs/urlform.js').FormParameter} FormParameter\n@typedef {import('../../../pkg/urlformjs/urlform.js').FormParameters} FormParameters\n@typedef {import('../../../pkg/urlformjs/urlform.js').FormOptions} FormOptions\n@typedef {import('../lib/typedef.js').ACOptions} ACOptions\n@typedef {import('../lib/typedef.js').PJArgs} PJArgs\n@typedef {import('./label.js').CustomPrintParams} CustomPrintParams\n*/\n\n/**\nACNormal holds the coze normal components, for an AC, as a JSON object.\n\n- need: Required coze.Pay fields.\n- optional: Optional coze.Pay fields.\n- extra: Extra coze.Pay fields (additional from required).\n- normal: AC's normal/canonical fields for coze.Pay.\n- pay: AC's signed payload from all other fields.\n@typedef {object} ACNormal\n@property {object} need\n@property {object} optional\n@property {object} extra\n@property {Can} normal\n@property {Pay} pay\n*/\n\n\n/**\nACFormOptions is used on many pages, so the definition is in /app instead of\n/page. \n@type {FormOptions} \n*/\nconst ACFormOptions = {\n\t\"id\": \"#OptionsForm\",\n\t\"prefix\": 'input_', // TODO deprecate input_, old old old, don't need this any more. \n\t\"FormParameters\": [{\n\t\t\t\"name\": \"is_model\",\n\t\t\t\"type\": \"bool\",\n\t\t\t\"funcTrue\": () => ToggleVisible('input_is_model_true'),\n\t\t},\n\t\t{\n\t\t\t\"name\": \"model_name\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"short_name\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"has_model\",\n\t\t\t\"type\": \"bool\",\n\t\t\t\"funcTrue\": () => ToggleVisible('input_model_parent_true'),\n\t\t},\n\t\t{\n\t\t\t\"name\": \"model_parent\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"has_title\",\n\t\t\t\"type\": \"bool\",\n\t\t\t\"funcTrue\": () => ToggleVisible('input_has_title_true'),\n\t\t},\n\t\t{\n\t\t\t\"name\": \"title\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"is_markdown\",\n\t\t\t\"type\": \"bool\",\n\t\t\t\"funcTrue\": () => ToggleVisible('input_is_markdown_true'),\n\t\t},\n\t\t{\n\t\t\t\"name\": \"markdown_dig\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"is_counterfeit\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"is_stolen\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t// Blacklist / Whitelist options\n\t\t// Users have to refresh the page on updates to blacklist/whitelist options.\n\t\t{\n\t\t\t\"name\": \"lock_comments\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"disable_comments\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"disable_authentic\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"disable_images\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"disable_files\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"disable_json_details\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"disable_brand_logo\",\n\t\t\t\"type\": \"bool\",\n\t\t\t\"funcTrue\": () => ToggleVisibleIfExists('hide_brand_logo'),\n\t\t},\n\t\t{\n\t\t\t\"name\": \"brand_logo\",\n\t\t},\n\t\t// Cyphrme logo may be used as a special case in the future, and always sent\n\t\t// from the server.\n\t\t{\n\t\t\t\"name\": \"disable_cyphrme_logo\",\n\t\t\t\"type\": \"bool\",\n\t\t},\n\t\t{\n\t\t\t\"name\": \"extra_options_json\"\n\t\t},\n\t\t{ // Must go after other options.\n\t\t\t\"name\": \"highlight\",\n\t\t\t\"type\": \"bool\",\n\t\t\t\"funcTrue\": async () => {\n\t\t\t\tdocument.querySelector(\"#printHighlightCSS\").disabled = false\n\t\t\t}\n\t\t},\n\t],\n}\n\n// AC Form params that are used for checks, counters, and other various\n// application uses, that are not signed themselves, but for determining other\n// fields to be signed.\nconst BLACKLIST_ACApplicationFormParameters = [\n\t\"isSerial\",\n\t\"serialStart\",\n]\n\n// Custom Print Form Options Form Parameters.\n// TODO Deprecate now that URLFormJS can handle multiple forms on a page. After\n// deprecating, GetACOptions should be as simple as returning Form.GetForm()\nconst BLACKLIST_ACPrintOptionFormParameters = [\n\t\"pageHeight\",\n\t\"pageWidth\",\n\t\"pageMarginTop\",\n\t\"pageMarginBottom\",\n\t\"pageMarginTopPaddingTop\",\n\t\"pageMarginBottomPaddingTop\",\n\t\"pageInnerPaddingLeft\",\n\t\"startAtLabel\",\n\t\"pageLandscape\",\n\t\"highlight\",\n\t\"outline\",\n\t\"noHeader\",\n\t\"blankHeader\",\n\t\"labelHeight\",\n\t\"labelWidth\",\n\t\"labelAuthTitleHeight\",\n\t\"labelAuthTitlePaddingTop\",\n\t\"labelMarginLeft\",\n\t\"labelContentsPaddingTop\",\n\t\"labelContentsMarginLeft\",\n\t\"labelLogoHeight\",\n\t\"labelTitleLogoHeight\",\n\t\"labelInnerPaddingTop\",\n\t\"labelInnerPaddingLeft\",\n\t\"labelQRSize\",\n\t\"labelVertical\",\n\t\"labelRotate90\",\n\t\"labelTitleLogo\",\n\t\"labelTitleLogoImageLink\",\n\t\"labelBrandingLevel\",\n\t\"labelCustomLogo\",\n\t\"labelCustomLogoImageLink\",\n\t\"labelTemplate\",\n\t// Extra print options not in main custom print options form.\n\t\"getModelLabelDescription\",\n\t\"labelDescription\",\n]\n\n\n// TODO Deprecate now that URLFormJS can handle multiple forms on a page. After\n// deprecating, GetACOptions should be as simple as returning Form.GetForm()\n//\n// AC Print Options FormParameters defined in ac_gen.js are blacklisted from\n// this file, also referred to as the AC Attributes Options Form.\nconst BLACKLIST_ACFormParameters = [\n\t\"advanced\",\n\t\"numberOfACsInput\",\n\t\"uploadBatchSize\",\n\t\"genTimeout\",\n\t\"urlInput\",\n\t\"generateQR\",\n\t\"resignIats\",\n\t// Batch\n\t\"isPJ\",\n\t\"DeterministicBatch\",\n\t\"deterministicSeed\",\n\t\"batchPages\",\n\t\"batchDepthSizes\",\n\t\"count\",\n\t...BLACKLIST_ACPrintOptionFormParameters\n]\n\n/** Holds the current Coze Normal state of the AC.\n@type {ACNormal} \n*/\nvar ACNormal = {}\n\n/**Holds the form's keys. */\nvar formKeys = []\n\n////////////////////// !! Important !! ///////////////////////////////////////\n////\n// InitSubmitOptions must be called in order to set the SubmitOptions\n// callback, as well as set the event listener for the update options button to\n// call the SubmitOptions Function that was set.\n\n// updateButton is the button for updating something from the options form\nconst updateButton = document.querySelector('#OptionUpdateBtn')\n\n//// Defaults\n// Default function that logs an error message if no SubmitOptionsFunction\n// is set.\nvar submitOptionsCallback = () => console.error(\"Must set a callback\")\n\n// This file is for modifying one AC at a time. This is the AC that is set\n// before being modified. For variadic, SetOriginalAC must be called for each\n// AC being modified.\nvar originalAC = {}\n\n// Modules are read-only, so we must set the variables here, with a setter.\nfunction InitSubmitOptions(SubmitOptionsCallback) {\n\tsubmitOptionsCallback = SubmitOptionsCallback\n}\n\n// Keep setting originalAC separate from setting the submitOptionsCallback,\n// to provide variadic capability. The callback should only be set once, but\n// the ac can be set many times.\nfunction SetOriginalAC(originalac) {\n\toriginalAC = originalac\n}\n\n// Initialized FormOptions for AC. Must be given on InitACOptions.\nvar initedFormOptions\n\n/**\nOnload/Initialization function for the ac options form. Optional parameters for\nnot initializing the show options form button, and the update button for the\noptions form. If these are passed in as true, the module importing this module\nneeds to do custom logic for the buttons and behavior.\n@param {FormOptions} formOptions\n@param {boolean} skipUpdateBtn=false // TODO it would be better if this option was handled by this function. \n@param {boolean} skipShowOptsBtn=false\n */\nasync function InitACOptions(formOptions, skipUpdateBtn) {\n\t// console.debug(skipUpdateBtn)\n\n\tinitedFormOptions = formOptions\n\n\t// Sanitize for undefined, etc.\n\tif (isEmpty(skipUpdateBtn)) {\n\t\tskipUpdateBtn = false\n\t}\n\t// Extra form parameters \n\tfor (let i of formOptions.FormParameters) {\n\t\tformKeys.push(i.name)\n\t}\n\t// Sets update button globally for page load, for owner to be able to access\n\t// this wherever necessary on the page.\n\tif (!skipUpdateBtn) {\n\t\tShow(updateButton)\n\t\t// Adds the event listener for when the update options button is clicked.\n\t\tupdateButton.addEventListener('click', async (event) => {\n\t\t\tevent.preventDefault()\n\t\t\t// Submit update AC Options action.\n\t\t\t// Throws an error if they AccountID does not own the AC\n\t\t\tif (Login.UAD != originalAC.uad) {\n\t\t\t\tCyphrme.Error('error: only the owner can update the Anti-Counterfeit')\n\t\t\t}\n\n\t\t\t// Set standard Pay\n\t\t\tlet standard = {\n\t\t\t\talg: originalAC.coze.pay.alg,\n\t\t\t\tiat: originalAC.coze.pay.iat,\n\t\t\t\ttmb: originalAC.coze.pay.tmb,\n\t\t\t\ttyp: originalAC.coze.pay.typ,\n\t\t\t\tid: originalAC.coze.pay.id\n\t\t\t}\n\n\t\t\t//// Debugging\n\t\t\t// console.debug(await CVA.ACUpdate(standard)))\n\t\t\t// return\n\n\t\t\tlet formData = new(FormData)\n\t\t\tformData.append('coze', JSON.stringify(await CVA.ACUpdate(standard)))\n\t\t\tsubmitOptionsCallback(await Ajax.FetchPost(Cyphrme.API.Post.ACPageUpdate, formData))\n\t\t})\n\t}\n\n\t// Toggle Options form checkbox element inputs.\n\tdocument.getElementById('input_disable_brand_logo').addEventListener('click', () => ToggleVisible('input_brand_logo_true'))\n\tdocument.getElementById('input_is_model').addEventListener('click', () => ToggleVisible('input_is_model_true'))\n\tdocument.getElementById('input_has_model').addEventListener('click', () => ToggleVisible('input_model_parent_true'))\n\tdocument.getElementById('input_has_title').addEventListener('click', () => ToggleVisible('input_has_title_true'))\n\t//document.getElementById('input_is_markdown').addEventListener('click', () => ToggleVisible('input_is_markdown_true'))\n\tdocument.getElementById('starter_pack').addEventListener('click', () => ToggleVisible('starter_pack_true'))\n\n\t// Toggle AC Options form\n\n\t// Checks whether or not the model_parent meets very basic requirements before\n\t// allowing the user to set this.\n\tdocument.querySelector('#input_model_parent').addEventListener('input', () => {\n\t\tlet modelID = document.querySelector('#input_model_parent').value\n\t\tif (!Cyphrme.IsCyphrmeDigest(modelID)) {\n\t\t\tupdateButton.disabled = true\n\t\t\tCyphrme.Notification(\"The Model ID is not correct.\", \"error\")\n\t\t\treturn\n\t\t}\n\t\tif (modelID == originalAC.id) {\n\t\t\tupdateButton.disabled = true\n\t\t\tCyphrme.Notification(\"You cannot make the Model ID the same as the current ID.\", \"error\")\n\t\t\treturn\n\t\t}\n\t\tupdateButton.disabled = false\n\t})\n\n\tdocument.querySelector('#input_brand_logo').addEventListener('input', function () {\n\t\tif (this.value !== \"\" && !Cyphrme.IsCyphrmeDigest(this.value)) {\n\t\t\tupdateButton.disabled = true\n\t\t\tCyphrme.Notification(\"The brand logo digest is not a valid digest.\", \"error\")\n\t\t\treturn\n\t\t}\n\t\tupdateButton.disabled = false\n\t})\n\n\t// Recalculate Extra fields once free form area is valid JSON.\n\tdocument.getElementById('input_extra_options_json').addEventListener('input', async (item) => {\n\t\tlet validJSON = true\n\t\tif (!isEmpty(item.target.value)) {\n\t\t\ttry {\n\t\t\t\tvar parsd = JSON.parse(item.target.value)\n\t\t\t} catch (error) {\n\t\t\t\tvalidJSON = false\n\t\t\t}\n\t\t\tif (!validJSON) {\n\t\t\t\tShow('freeFormValidJSONWarning')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tACNormal.extra = parsd\n\t\t} else {\n\t\t\tACNormal.extra = {}\n\t\t}\n\t\tHide('freeFormValidJSONWarning')\n\t\tRecalcOptionGUI(await recalculatePayFromACNormal())\n\t})\n\n\t// Reprocess AC Options on change\n\tdocument.getElementById('OptionsForm').addEventListener('input', async () => {\n\t\ttry {\n\t\t\tvar newAC = await ProcessOpts(ACNormal, await ACGetOptionsExtra())\n\t\t\t// console.debug(newAC)\n\t\t} catch (error) {\n\t\t\tconsole.error(error)\n\t\t\treturn\n\t\t}\n\t\tRecalcOptionGUI(newAC.pay)\n\t})\n\n}\n\n\n/**\nAcsFromTree returns an object of populated Anti-Counterfeits, from the given\ntree. Each AC contains it's page and batch information from the tree.\n@param {PJArgs} pjArgs\n@returns {ACsObj}\n@throws {error}\n*/\nasync function AcsFromTree(pjArgs) {\n\t// console.debug(pjArgs)\n\tif (isEmpty(pjArgs.tree.leavesID)) {\n\t\tthrow new Error('AcsFromTree: Tree.leavesID is empty. (pathCalc must be true on tree generation for leavesID to be set.)')\n\t}\n\n\tlet acs = {}\n\tfor (let id of pjArgs.tree.leavesID) {\n\t\tlet ac = await Lib.NewAnticounterfeit(Login.CozeKey, {\n\t\t\tid: id // Don't gen ac with random ID.\n\t\t})\n\t\tac = await ProcessOpts(ac, pjArgs.ac_options)\n\t\t// console.debug(ac)\n\t\tac.qr_uri = Cyphrme.QRCodeURL\n\t\tac.base37 = Lib.BASE37Padded(await Lib.B64ToHex(ac.pay.id), Lib.AB.Base16, ac.pay.alg)\n\t\tac.pj_id = pjArgs.id // pj_id is outside pay, and never signed. (AC's are associated to job via ticket in the database, but the field isn't signed)\n\t\tacs[id] = ac\n\t}\n\treturn acs\n}\n\n\n/**\nTakes a model parent/ model ID and returns whether or not the model seems\nlikely to be a valid model ID, and is not the same as the child's ID.\n@param {B64} id ID of the model parent.\n@returns {boolean}\n */\nfunction CheckModelParent(id) {\n\tif (!Cyphrme.IsCyphrmeDigest(id)) {\n\t\t// console.debug(\"The Model ID is not correct.\")\n\t\treturn false\n\t}\n\tif (id == originalAC.id) {\n\t\t// console.debug(\"You cannot make the Model ID the same as the current ID.\")\n\t\treturn false\n\t}\n\treturn true\n}\n\n/**\nACGetOptions returns ACOptions from the ac options form.\nThis does not include Extra fields. If needing the full AC Options form,\nincluding extra fields, use ACGetOptionsExtra() instead.\n\n'URLFormJS' must be initialized (outside of this module) to call this function.\n\n'extra_options_json' is used in the options form for holding extra fields,\nbut excluded from the ACOptions returned. To get extra fields, use\nACGetOptionsExtra().\n@returns {ACOptions}\n@throws {error}\n*/\nfunction ACGetOptions() {\n\t// TODO namespace for ac in HTML\n\tlet obj = URLForm.GetForm(initedFormOptions) // throws\n\tlet acOptions = {}\n\tfor (let key in obj) {\n\t\tif (BLACKLIST_ACFormParameters.includes(key) || key === 'extra_options_json') {\n\t\t\tcontinue\n\t\t}\n\t\tacOptions[key] = obj[key]\n\t}\n\treturn acOptions\n}\n\n/**\nACGetBlacklistedPrintOptions returns only the blacklisted ac print options that\nare elided from `ACGetOptions` and `ACGetOptionsExtra`. Blacklisted AC print\noptions are only signed when the selected prefined label size is `Custom`. To\nbe able to recreate the sticker, all of the print options are signed.\n\nTODO Split AC Print Options out into a seperate form that can just be called\nwith GetForm to avoid the blacklists.\n@returns {CustomPrintParams}\n@throws {error}\n*/\nfunction ACGetBlacklistedPrintOptions() {\n\tlet obj = URLForm.GetForm(initedFormOptions) // throws\n\tlet printOpts = {}\n\tfor (let key in obj) {\n\t\tif (BLACKLIST_ACPrintOptionFormParameters.includes(key)) {\n\t\t\tprintOpts[key] = obj[key]\n\t\t\tcontinue\n\t\t}\n\t}\n\treturn printOpts\n}\n\n/**\nACGetOptions returns a parsed ACOptions object from the full AC options form,\nincluding extra fields.\n@returns {ACOptions}\n@throws {error}\n */\nfunction ACGetOptionsExtra() {\n\tlet optional = ACGetOptions() // throws\n\tlet ex = document.getElementById('input_extra_options_json').value\n\tif (!isEmpty(ex)) {\n\t\tlet extra = JSON.parse(ex)\n\t\tfor (let v in extra) {\n\t\t\tif (isEmpty(optional[v])) {\n\t\t\t\toptional[v] = extra[v]\n\t\t\t}\n\t\t}\n\t}\n\treturn optional\n}\n\n/**\nACRegenPay accepts an old pay, and a parsed Options form. The fields in the\nparsed form are added to a copy of the old pay passed in, and the new pay are\nreturned after adding the parsed fields.\n@param {Pay} oldPay Old/current Coze Pay.\n@param {ACOptions} opts Parsed Options form.\n@param {string} [typ] Optional typ. Defaults to \"cyphr.me/ac/create\"\n@returns {Pay}\n */\nasync function ACRegenPay(oldPay, opts, typ) {\n\t// console.debug(\"OldPay: \", oldPay, \"Opts: \", opts, \"Typ:\", typ)\n\tif (isEmpty(typ)) {\n\t\ttyp = CVA.Typs.TypACCreate\n\t}\n\tlet pay = {\n\t\talg: oldPay.alg,\n\t\tiat: oldPay.iat,\n\t\ttmb: oldPay.tmb,\n\t\ttyp: typ,\n\t\tid: oldPay.id\n\t}\n\n\t// Set new options\n\n\t// See `ProcessOpts` docs on `isSerial`.\n\tif (opts.isSerial) {\n\t\tpay.serial = opts.serial\n\t}\n\n\tif (opts.has_model) {\n\t\tif (Cyphrme.IsBASE37(opts.model_parent)) {\n\t\t\topts.model_parent = await Lib.HexTob64ut(await Lib.HexPadded(opts.model_parent, Lib.AB.BASE37, Login.CozeKey.alg))\n\t\t}\n\t\tpay.has_model = opts.has_model\n\t\tpay.model_parent = opts.model_parent\n\t}\n\tif (opts.is_model) {\n\t\tpay.is_model = opts.is_model\n\t\tpay.model_name = opts.model_name\n\t}\n\tif (opts.has_title) {\n\t\tpay.title = opts.title\n\t}\n\tif (opts.is_markdown) {\n\t\tif (isEmpty(opts.markdown_dig)) {\n\t\t\tthrow new Error(\"markdown digest must be populated if ac is a markdown page\")\n\t\t}\n\t\tpay.is_markdown = true\n\t\tpay.markdown_dig = opts.markdown_dig\n\t} else {\n\t\tdelete pay.is_markdown\n\t\tdelete pay.markdown_dig\n\t}\n\tif (opts.is_counterfeit) {\n\t\tpay.is_counterfeit = opts.is_counterfeit\n\t}\n\tif (opts.is_stolen) {\n\t\tpay.is_stolen = opts.is_stolen\n\t}\n\n\t// AC Blacklist fields\n\tif (opts.lock_comments) {\n\t\tpay.lock_comments = opts.lock_comments\n\t}\n\tif (opts.disable_comments) {\n\t\tpay.disable_comments = opts.disable_comments\n\t}\n\tif (opts.disable_authentic) {\n\t\tpay.disable_authentic = opts.disable_authentic\n\t}\n\tif (opts.disable_images) {\n\t\tpay.disable_images = opts.disable_images\n\t}\n\tif (opts.disable_files) {\n\t\tpay.disable_files = opts.disable_files\n\t}\n\tif (opts.disable_json_details) {\n\t\tpay.disable_json_details = opts.disable_json_details\n\t}\n\tif (opts.disable_brand_logo) {\n\t\tpay.disable_brand_logo = opts.disable_brand_logo\n\t\tdelete pay.brand_logo\n\t} else {\n\t\tdelete pay.disable_brand_logo\n\t}\n\tif (\"brand_logo\" in opts && (!opts.disable_brand_logo)) {\n\t\tpay.brand_logo = opts.brand_logo\n\t}\n\tif (opts.disable_cyphrme_logo) {\n\t\tpay.disable_cyphrme_logo = opts.disable_cyphrme_logo\n\t}\n\n\t// Add extra fields\n\tfor (let key in opts) {\n\t\tif (!formKeys.includes(key)) {\n\t\t\tpay[key] = opts[key]\n\t\t}\n\t}\n\n\treturn pay\n}\n\n/**\nTODO Run through exclusions for when one type is checked, any excluded options\nget disabled.\n\nProcessOpts processes over the given AC Options Form Object, and add any of the\nfields (if applicable) to the given ac, and return the newly modified ac,\nresigned. If the ac has not been modified, it is not resigned, and the original\nis returned.\n\nElse if statements are used for removing items that may have been\nremoved/deleted from the object from user modifications. Returns new AC, being\nresigned if updates occurred.\n\nCoze does not allow resigning of an AC if the tmb from the current AC does not\nmatch the Coze Key signing the current payload. The logic below sets the\npayloads tmb to the current Coze Key, if the original AC's tmb is different and\neither the `uad` of the AC also owns the current Coze Key.\n@param {Coze} ac Coze object.\n@param {ACOptions} opts Parsed from the AC options form.\n@returns {ac}\n */\nasync function ProcessOpts(ac, opts) {\n\t// console.debug(\"AC: \", ac, \"Options: \", opts)\n\tif (isEmpty(opts)) {\n\t\topts = {}\n\t}\n\t// Initialize counter `serial` for serialization when`opts` first given.\n\tif (\"isSerial\" in opts && !(\"serial\" in opts)) {\n\t\tif (!(\"serialStart\" in opts)) {\n\t\t\topts.serialStart = 0\n\t\t}\n\t\topts.serial = opts.serialStart\n\t}\n\tvar acNotGenerated = false\n\tif (isEmpty(ac.pay) || isEmpty(ac.pay.id)) {\n\t\tacNotGenerated = true\n\t\tac.pay = {}\n\t}\n\n\t// Resign ACs with current logged in key, instead of original key that signed,\n\t// if owner.\n\tif (!acNotGenerated && ac.pay.tmb !== Login.CozeKey.tmb) {\n\t\tif (!isEmpty(originalAC) && Login.UAD === originalAC.uad) {\n\t\t\tconsole.debug(`Original 'tmb' is different from current 'tmb', using current 'tmb'.`)\n\t\t\tac.pay.tmb = Login.CozeKey.tmb\n\t\t} else {\n\t\t\tconsole.error('Must be owner to update the AC.')\n\t\t\treturn\n\t\t}\n\t}\n\n\t//// Options\n\t// if (!isEmpty(opts)) {\n\tvar resign = false\n\n\t// AC Print Opts\n\n\t// NOTE: Current convention is for AC Options to be lower snake cased, while\n\t// AC Print Options are lower camel cased. Most AC Print Options are not\n\t// signed, except for on a model, or PJ. There may be exceptions, such as\n\t// `serial` on individual ACs. Incrementers on opts must be incremented after\n\t// signing, or the incremented value is what gets signed. Seems JS has a race\n\t// condition when using the same incrementer reference.\n\tif (opts.isSerial) {\n\t\tac.pay.serial = opts.serial\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.serial)) {\n\t\tdelete ac.pay.serial\n\t}\n\n\t// AC Opts\n\n\tif (opts.has_model) {\n\t\tif (!isEmpty(opts.model_parent)) {\n\t\t\tif (!Cyphrme.IsCyphrmeDigest(opts.model_parent)) {\n\t\t\t\tCyphrme.Error(\"parent is an unsupported digest\")\n\t\t\t}\n\t\t\tac.pay.model_parent = opts.model_parent\n\t\t\tif (Cyphrme.IsBASE37(opts.model_parent)) {\n\t\t\t\tac.pay.model_parent = await Lib.HexTob64ut(await Lib.HexPadded(opts.model_parent, Lib.AB.BASE37, Login.CozeKey.alg))\n\t\t\t}\n\t\t\tresign = true\n\t\t}\n\t} else if (!isEmpty(ac.pay.model_parent)) {\n\t\tdelete ac.pay.model_parent\n\t}\n\n\tif (opts.is_model) {\n\t\tac.pay.is_model = true\n\t\tif (!isEmpty(opts.model_name)) {\n\t\t\tac.pay.model_name = opts.model_name\n\t\t} else if (!isEmpty(ac.pay.model_name)) {\n\t\t\tdelete ac.pay.model_name\n\t\t}\n\n\t\tif (!isEmpty(opts.short_name)) {\n\t\t\tac.pay.short_name = opts.short_name\n\t\t} else if (!isEmpty(ac.pay.short_name)) {\n\t\t\tdelete ac.pay.short_name\n\t\t}\n\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.is_model)) {\n\t\tdelete ac.pay.is_model\n\t\tdelete ac.pay.short_name\n\t\tdelete ac.pay.model_name\n\t}\n\n\tif (opts.is_markdown && !isEmpty(opts.markdown_dig)) {\n\t\tif (!Cyphrme.IsCyphrmeDigest(opts.markdown_dig)) {\n\t\t\tCyphrme.Error(\"markdown digest is an unsupported digest\")\n\t\t}\n\t\tac.pay.markdown_dig = opts.markdown_dig\n\t\tac.pay.is_markdown = true\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.markdown_dig)) {\n\t\tdelete ac.pay.markdown_dig\n\t\tdelete ac.pay.is_markdown\n\t}\n\tif (opts.has_title && !isEmpty(opts.title)) {\n\t\tac.pay.title = opts.title\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.title)) {\n\t\tdelete ac.pay.title\n\t}\n\tif (opts.is_counterfeit) {\n\t\tac.pay.is_counterfeit = opts.is_counterfeit\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.is_counterfeit)) {\n\t\tdelete ac.pay.is_counterfeit\n\t}\n\tif (opts.is_stolen) {\n\t\tac.pay.is_stolen = opts.is_stolen\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.is_stolen)) {\n\t\tdelete ac.pay.is_stolen\n\t}\n\n\t// AC Blacklist values\n\tif (opts.lock_comments) {\n\t\tac.pay.lock_comments = opts.lock_comments\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.lock_comments)) {\n\t\tdelete ac.pay.lock_comments\n\t}\n\tif (opts.disable_comments) {\n\t\tac.pay.disable_comments = opts.disable_comments\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.disable_comments)) {\n\t\tdelete ac.pay.disable_comments\n\t}\n\tif (opts.disable_authentic) {\n\t\tac.pay.disable_authentic = opts.disable_authentic\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.disable_authentic)) {\n\t\tdelete ac.pay.disable_authentic\n\t}\n\tif (opts.disable_images) {\n\t\tac.pay.disable_images = opts.disable_images\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.disable_images)) {\n\t\tdelete ac.pay.disable_images\n\t}\n\tif (opts.disable_files) {\n\t\tac.pay.disable_files = opts.disable_files\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.disable_files)) {\n\t\tdelete ac.pay.disable_files\n\t}\n\tif (opts.disable_json_details) {\n\t\tac.pay.disable_json_details = opts.disable_json_details\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.disable_json_details)) {\n\t\tdelete ac.pay.disable_json_details\n\t}\n\tif (opts.disable_brand_logo) {\n\t\tac.pay.disable_brand_logo = opts.disable_brand_logo\n\t\tdelete ac.pay.brand_logo\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.disable_brand_logo)) {\n\t\tdelete ac.pay.disable_brand_logo\n\t}\n\tif (\"brand_logo\" in opts && !opts.disable_brand_logo) {\n\t\tac.pay.brand_logo = opts.brand_logo\n\t\tresign = true\n\t}\n\n\tif (opts.disable_cyphrme_logo) {\n\t\tac.pay.disable_cyphrme_logo = opts.disable_cyphrme_logo\n\t\tresign = true\n\t} else if (!isEmpty(ac.pay.disable_cyphrme_logo)) {\n\t\tdelete ac.pay.disable_cyphrme_logo\n\t}\n\n\n\t//// Extras\n\tlet currentSupportedOptions = []\n\tfor (let p of ACFormOptions.FormParameters) {\n\t\tcurrentSupportedOptions.push(p.name)\n\t}\n\t// console.debug(currentSupportedOptions)\n\t// console.debug(opts)\n\tfor (let param in opts) {\n\t\tif (!currentSupportedOptions.includes(param) && !BLACKLIST_ACApplicationFormParameters.includes(param)) {\n\t\t\tac.pay[param] = opts[param]\n\t\t}\n\t}\n\n\t// Resign if AC has been modified, and is generated already.\n\tif (resign && !acNotGenerated) {\n\t\tdelete ac.pay.seed\n\t\tdelete ac.pay.base37\n\t\tac = await Coze.Sign(ac, Login.CozeKey)\n\t}\n\t// Increment after signing, so that reference to incrementer is not used.\n\tif (opts.isSerial && resign) {\n\t\topts.serial++\n\t}\n\treturn ac\n}\n\n/**\nRecalculate the AC Pay from the current ACNormal state.\nThe pay is constructed in the following order:\n-Need (Required fields)\n-Optional (Optional fields)\n-Extra (Extra fields)\n@returns {Pay} pay Coze Pay with AC Normalized fields.\n */\nasync function recalculatePayFromACNormal() {\n\tlet pay = {\n\t\t...ACNormal.need\n\t}\n\tif (!isEmpty(ACNormal.optional)) {\n\t\tfor (let v in ACNormal.optional) {\n\t\t\tpay[v] = ACNormal.optional[v]\n\t\t}\n\t}\n\tif (!isEmpty(ACNormal.extra)) {\n\t\tfor (let v in ACNormal.extra) {\n\t\t\tpay[v] = ACNormal.extra[v]\n\t\t}\n\t}\n\treturn pay\n}\n\n/**\nCalculates (or recalculates) and sets the ACNormal object from the given pay.\nThe pay should be the full AC Pay.\n\nIf the given pay is empty, ACNormal and the user READ only JSON area are\nreset/cleared.\n\nSets the AC Options form GUI from the split out components.\n\nTODO Run through isNormal() after splitting into various components.\n@param {Pay} pay Coze Pay with an Anti-Counterfeit Coze Normal.\n@returns {void}\n */\nfunction RecalcOptionGUI(pay) {\n\tlet userJSON = {}\n\tlet optInfoElem = document.getElementById('optionsInfo')\n\t// AC Gen page may have no options and no AC generated yet.\n\tif (isEmpty(pay)) {\n\t\tACNormal = {}\n\t\toptInfoElem.textContent = \"\"\n\t\treturn\n\t}\n\n\t// Split AC into Coze Normal format.\n\tlet normal = [\"alg\", \"iat\", \"tmb\", \"typ\", \"id\"]\n\n\t// Required fields\n\tlet need = {}\n\t// Check if AC has been generated yet.\n\tif (!isEmpty(pay.id)) {\n\t\tneed = {\n\t\t\talg: pay.alg,\n\t\t\tiat: pay.iat, // AC iat, not coze iat\n\t\t\ttmb: pay.tmb,\n\t\t\ttyp: pay.typ,\n\t\t\tid: pay.id,\n\t\t}\n\t}\n\n\t// Optional fields\n\tlet optional = {}\n\n\tfor (let opt of ACFormOptions.FormParameters) {\n\t\tif (opt.name in pay) {\n\t\t\tnormal.push(opt.name)\n\t\t\toptional[opt.name] = pay[opt.name]\n\t\t\tuserJSON[opt.name] = pay[opt.name]\n\t\t}\n\t}\n\n\t// Extra fields\n\tlet extra = {}\n\tfor (let v in pay) {\n\t\tif (!normal.includes(v)) {\n\t\t\tnormal.push(v)\n\t\t\textra[v] = pay[v]\n\t\t\tuserJSON[v] = pay[v]\n\t\t}\n\t}\n\n\t// Set Extra sticky JSON text area for when page loads and AC has extra fields.\n\tif (!isEmpty(extra)) {\n\t\tdocument.getElementById('input_extra_options_json').value = JSON.stringify(extra, null, 2)\n\t}\n\n\t// Set GUI READ ONlY JSON area for displaying what optional/extra fields are\n\t// signed for the AC.\n\tif (!isEmpty(userJSON)) {\n\t\toptInfoElem.textContent = JSON.stringify(userJSON, null, 2)\n\t} else {\n\t\toptInfoElem.textContent = \"\"\n\t}\n\n\t// Set global state\n\tACNormal = {\n\t\tneed: need, // Required fields.\n\t\toptional: optional, // Optional fields.\n\t\textra: extra, // Extra fields not specified in optional.\n\t\tnormal: normal, // Coze.Normal Requirements / Canon\n\t\tpay: pay // Original Pay used to construct this Object.\n\t}\n}\n\n/**\nReturns AC sticker title, if applicable.\n\nSticker titles returned have a maximum of 12 characters.\n\nThe returned title for the sticker is chosen in the following order:\nLabel Description -> Short Name -> Model Name -> Title -> SKU\n\nFor handling the main or child AC logic, pass in the AC, or AC's latest Coze.\nIf an AC has a parent, and is wanting to be used, the parent's AC or Coze\nshould be given, and not the child AC.\n@param {AC|Coze} ac Anti-Counterfeit object.\n@returns {string}\n */\nfunction GetStickerTitle(ac) {\n\t// console.debug(\"AC: \", ac)\n\t// TODO think about: if ac.coze not empty do parent logic and recall this func with ac.coze\n\tlet stickerTitle = \"\"\n\tlet ld = document.getElementById('input_labelDescription')\n\tif (ld !== null && !isEmpty(ld.value)) {\n\t\tstickerTitle = ld.value\n\t} else if (!isEmpty(ac.pay.short_name)) {\n\t\tstickerTitle = ac.pay.short_name\n\t} else if (!isEmpty(ac.pay.model_name)) {\n\t\tstickerTitle = ac.pay.model_name\n\t} else if (!isEmpty(ac.pay.title)) {\n\t\tstickerTitle = ac.pay.title\n\t} else if (!isEmpty(ac.pay.sku)) {\n\t\tstickerTitle = ac.pay.sku\n\t}\n\treturn stickerTitle\n}", "\"use strict\";\n\n// This module holds all of the Cyphr.me CVA's (cryptographically verifiable\n// actions). CVA's are cozies. \n\n/** \n@typedef {import('../../../pkg/cozejs/typedef.js').Coze} Coze\n@typedef {import('../../../pkg/cozejs/typedef.js').Pay} Pay\n@typedef {import('../../../pkg/cozejs/typedef.js').Canon} Canon\n@typedef {import('../../../pkg/cozejs/typedef.js').B64} B64\n@typedef {import('../../../pkg/cozejs/typedef.js').Key} Key\n\n@typedef {import('./login.js').Profile} Profile\n@typedef {import('./comment.js').CommentPay} CommentPay\n*/\n\nimport * as Login from './login.js';\nimport * as Cyphrme from './cyphrme.js';\nimport * as File from './file.js';\nimport * as ACOpts from './ac_options.js';\n\nimport * as Coze from '../pkg/coze_all~fv=-7uCIHNa.min.js'\nimport * as Lib from '../lib/lib~fv=LNbMt1n5.min.js';\n\nexport {\n\t// Supported Cyphr.me Coze `typ`s.\n\tTyps,\n\n\t// Helpers\n\tGenCoze,\n\tGenCozeWithMeta,\n\tGenPayStandard,\n\n\t// ACs\n\tACUpdate,\n\n\t// PrintJobs\n\tPrintJobCreate,\n\tPrintJobUpdate,\n\tBundleCreate,\n\n\t// Key\n\tKeyUpsert,\n\tKeyDelete,\n\tKeyRevoke,\n\tKeyOtherRevoke,\n\n\t// User\n\tInviteCreate,\n\tInviteDelete,\n\tProfileUpdate,\n\tProfilePictureUpdate,\n\tEmailBackupCreate,\n\tEmailVerifyCreate,\n\n\t// Comment\n\tCommentCreate,\n\tCommentDelete,\n\tCommentUpdate,\n\n\t// File/Image\n\tFileCreate,\n\tFileDelete,\n};\n\n// Supported Cyphr.me Coze `typ`s.\nconst Typs = {\n\tTypACCreate: \"cyphr.me/ac/create\",\n\tTypACUpdate: \"cyphr.me/ac/update\",\n\tTypPJCreate: \"cyphr.me/pj/create\",\n\tTypPJUpdate: \"cyphr.me/pj/update\",\n\tTypBundleCreate: \"cyphr.me/bundle/create\",\n\tTypKeyUpsert: \"cyphr.me/key/upsert\",\n\tTypKeyDelete: \"cyphr.me/key/delete\",\n\tTypKeyRevoke: \"cyphr.me/key/revoke\",\n\tTypKeyOtherRevoke: \"cyphr.me/key/other/revoke\",\n\tTypUserInviteCreate: \"cyphr.me/user/invite/create\",\n\tTypUserInviteDelete: \"cyphr.me/user/invite/delete\",\n\tTypUserEmailBackupCreate: \"cyphr.me/user/email/backup/create\",\n\tTypUserEmailVerifyCreate: \"cyphr.me/user/email/verify/create\",\n\tTypUserProfileUpdate: \"cyphr.me/user/profile/update\",\n\tTypUserProfilePictureUpdate: \"cyphr.me/user/profile/picture/update\",\n\tTypCommentCreate: \"cyphr.me/comment/create\",\n\tTypCommentUpdate: \"cyphr.me/comment/update\",\n\tTypCommentDelete: \"cyphr.me/comment/delete\",\n\tTypFileCreate: \"cyphr.me/file/create\",\n\tTypFileDelete: \"cyphr.me/file/delete\",\n}\n\n\n\n/**\nGenCoze generates a signed `coze` from `pay`.\n@param {Pay} pay Coze pay.\n@param {Key} [Key] Coze key. Default is currently used key.\n@returns {Coze}\n@throws {error}\n */\nasync function GenCoze(pay, cozeKey) {\n\tif (isEmpty(cozeKey)) {\n\t\tcozeKey = Login.CozeKey;\n\t}\n\treturn Coze.Sign({\n\t\t\t\"pay\": pay\n\t\t},\n\t\tcozeKey);\n};\n\n/**\nGenCozeWithMeta generates a coze with `coze.czd` and `coze.cad` set.\n@param {Pay} pay\n@throws {error}\n@returns {Coze}\n */\nasync function GenCozeWithMeta(pay) {\n\tlet coze = await GenCoze(pay)\n\tlet meta = await Coze.Meta(coze, coze.pay.alg)\n\tcoze.czd = meta.czd\n\tcoze.cad = meta.cad\n\treturn coze\n}\n\n/**\nGenPayStandard returns a 'pay' object with 'alg', 'iat', and 'tmb' set in the\nstandard order. Uses login.js' coze key.\n@returns {Pay}\n */\nasync function GenPayStandard() {\n\treturn {\n\t\talg: Login.CozeKey.alg,\n\t\tiat: Now(),\n\t\ttmb: Login.CozeKey.tmb,\n\t};\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n// AC Cozies\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nACUpdate returns an AC Update coze from the given payload, and AC Options. `pay`\nmust be given initialized with standard coze fields populated, and additionally,\n`id`. `pay.typ` is not required and set in this function.\n@param {Pay} standard\n@returns {Coze}\n@throws {error} Fails if AC Options Form not initialized and loaded on the\npage.\n */\nasync function ACUpdate(standard) {\n\treturn GenCoze(await ACOpts.ACRegenPay(standard, ACOpts.ACGetOptionsExtra(), Typs.TypACUpdate))\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// PJ Cozes\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nPrintJobCreate signs a create PrintJob coze for uploading.\n\ncredits is included to 1. have an explicit acknowledgement of the cost and 2. to\nmake sure that there isn't a disagreement, which would be a bug, between client\nand server. \n\nTODO we probably need a flag to denote \"private\" cozies and eventually encrypt private fields. \n@param {PrintJob} pj Print job.\n@returns {Coze}\n */\nasync function PrintJobCreate(pj) {\n\t// console.debug(pj)\n\tlet pay = await GenPayStandard()\n\tpay.typ = Typs.TypPJCreate\n\tpay.id = pj.id\n\t// TODO Perhaps don't include seed. Seed doesn't need to be signed since id is\n\t// signed, but it needs to be transported with the Coze. Using HTTP transport\n\t// is not ideal, it would be better to include the field in the coze or beside\n\t// the coze. Seed is being signed at the moment solely for the purpose of\n\t// transport.\n\tpay.seed = pj.args.tree.seed\n\tpay.tree = pj.args.tree.id\n\tpay.credits = pj.credits\n\tpay.determ = pj.args.determ\n\tpay.branch_level_sizes = pj.args.tree.branch_level_sizes\n\tpay.ac_label_size = pj.args.ac_label_size\n\tpay.sbl = pj.args.ac_sticker_brand_level\n\tif (!isEmpty(pj.args.print_options)) {\n\t\tpay.print_opts = pj.args.print_options\n\t}\n\treturn GenCozeWithMeta(pay)\n}\n\n/**\nPrintJobUpdate signs an update PrintJob coze for uploading.\nTODO probably deprecate and don't allow an PJ update. \n{\"alg\", \"iat\", \"tmb\", \"typ\", \"id\", \"credits\", \"determ\", \"branch_level_sizes\", \"tree\", \"seed\", \"bundle_type\"}\n@param {PrintJob} pj Print job.\n@returns {Coze}\n */\nasync function PrintJobUpdate(pj) {\n\n\tlet pay = await GenPayStandard()\n\tpay.typ = Typs.TypPJUpdate\n\tpay.id = pj.id\n\tpay.credits = pj.credits\n\tpay.determ = pj.determ\n\tpay.branch_level_sizes = pj.branch_level_sizes\n\tpay.tree = pj.tree.id\n\tpay.seed = pj.seed\n\t// pay.bundle_type = pj.bundle_type\n\treturn GenCozeWithMeta(pay)\n}\n\n/**\nCreate signs a create Bundle coze for uploading.\nDon't need `pj_id` as it is stored on the bundle ticket and pulled from db.\n{\"alg\", \"iat\", \"tmb\", \"typ\", \"id\", \"parent\", \"determ\", \"bundle_type\", \"n_childs\", \"numer\", \"denom\"}\n@param {bundle} bundle\n@returns {Coze}\n */\nasync function BundleCreate(bundle) {\n\tlet pay = await GenPayStandard()\n\tpay.typ = Typs.TypBundleCreate\n\tpay.id = bundle.id\n\tpay.parent = bundle.parent\n\tpay.determ = bundle.determ\n\tpay.bundle_type = bundle.bundle_type\n\tpay.n_childs = bundle.n_childs\n\tpay.numer = bundle.numer\n\tpay.denom = bundle.denom\n\treturn GenCozeWithMeta(pay)\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Key Cozes\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nKeyUpsert returns a signed key upsert Coze. Upsert typs are used for create and\nedit actions.\n\nsignCozeKey is used to sign the upsert coze if given. This is currently only\nused in first time login.\n@param {CozeKey} upsertCozeKey\n@param {CozeKey} [signCozeKey]\n@returns {Coze}\n@protected {normal=[only]}\n */\nasync function KeyUpsert(upsertCozeKey, signCozeKey) {\n\tlet ck = {\n\t\t...upsertCozeKey\n\t}\n\t// console.debug(ck);\n\tdelete ck.d; // Sanity delete. No private.\n\tlet pay = await GenPayStandard();\n\tpay.typ = Typs.TypKeyUpsert\n\tpay.key = ck;\n\tif (upsertCozeKey.backup > 0) {\n\t\tpay.backup = upsertCozeKey.backup;\n\t}\n\treturn GenCoze(pay, signCozeKey);\n};\n\n/**\nKeyDelete deletes a key.\n@param {B64} tmb Thumbprint of key being deleted.\n@returns {Coze}\n@protected {normal=[only]}\n */\nasync function KeyDelete(tmb) {\n\treturn GenCoze({\n\t\ttyp: Typs.TypKeyDelete,\n\t\tid: tmb\n\t});\n};\n\n/**\nKeyRevoke generates a self revoke.\n@param {CozeKey} ck CozeKey being revoked.\n@param {string} [msg] Optional message for revoke reason.\n@returns {Coze}\n@protected {normal=[canon,option]}\n */\nasync function KeyRevoke(ck, msg) {\n\tlet pay = {\n\t\talg: ck.alg,\n\t\tiat: Now(),\n\t\ttmb: ck.tmb,\n\t\ttyp: Typs.TypKeyRevoke,\n\t\trvk: Now(),\n\t};\n\tif (!isEmpty(msg)) {\n\t\tpay.msg = msg;\n\t}\n\n\treturn Coze.Sign({\n\t\t\t\"pay\": pay\n\t\t},\n\t\tck);\n};\n\n\n/**\nKeyOtherRevoke generates a other revoke action. An other revoke is one key\nrevoking another key. Coze does not know whether or not a key is owned by an\nindividual, but the server does not allow this revoke transaction to happen,\nunless the key signing the payload owns the key being revoked. The only check\nthat JS can perform before sending off the request would be making sure that the\nkey is in the wallet, but since it can just be a tmb only key, that is not\nhelpful.\n\nThought: In the future when auth has permissions, maybe other revokes can assign\nwhich keys are allowed to revoke it.\n@param {B64} tmb Thumbprint for Coze Key being revoked.\n@param {string} [msg] Optional message for revoke reason.\n@returns {Coze}\n@protected {normal=[canon,option]}\n */\nasync function KeyOtherRevoke(tmb, msg) {\n\tlet pay = await GenPayStandard();\n\tpay.typ = Typs.TypKeyOtherRevoke;\n\tpay.rvk = Now();\n\tpay.id = tmb;\n\n\tif (!isEmpty(msg)) {\n\t\tpay.msg = msg;\n\t}\n\treturn GenCoze(pay);\n};\n\n\n////////////////////////////////////////////////////////////////////////////////\n// User\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nInviteCreate returns a signed Coze action for a user invite.\n@param {B64} uad User address digest.\n@param {string} displayName Display name for the user.\n@returns {Coze}\n@protected {normal=[canon,option]}\n */\nasync function InviteCreate(uad, displayName) {\n\tlet pay = await GenPayStandard();\n\tpay.typ = Typs.TypUserInviteCreate;\n\tpay.id = uad;\n\tif (!isEmpty(displayName)) {\n\t\tpay.display_name = displayName;\n\t}\n\treturn GenCoze(pay);\n};\n\n/**\nInviteDelete returns a signed delete invite Coze.\n@param {B64} id Uad for the user invite being deleted from the system.\n@returns {Coze}\n@protected {normal=[only]}\n */\nasync function InviteDelete(id) {\n\tlet pay = await GenPayStandard();\n\tpay.typ = Typs.TypUserInviteDelete;\n\tpay.id = id;\n\treturn GenCoze(pay);\n};\n\n/**\nEmailBackupCreate returns a coze for requesting account backup via email.\n@param {string} email Email address for sending the private key to.\n@returns {Coze}\n@protected {normal=[only]}\n */\nasync function EmailBackupCreate(email) {\n\tlet pay = await GenPayStandard();\n\tpay.typ = Typs.TypUserEmailBackupCreate;\n\tpay.email = email;\n\treturn GenCoze(pay);\n}\n\n/**\nEmailBackupCreate returns a coze for requesting account backup via email.\n@param {string} email Email address for sending the private key to.\n@returns {Coze}\n@protected {normal=[only]}\n */\nasync function EmailVerifyCreate(email) {\n\tlet pay = await GenPayStandard();\n\tpay.typ = Typs.TypUserEmailVerifyCreate;\n\tpay.email = email;\n\treturn GenCoze(pay);\n}\n\n/**\nProfileUpdate updates a user's profile.\nThe full profile should be given for updates, and not just the updated\nfields. If 'id' is the only populated field in the profile object,\nthat clears all Profile fields (except 'id').\n\nErrors if 'id' in the Profile object is empty, or unrecognized cyphrme hash.\n\nAPI Coze Normal Requirements for `cyphr.me/user/profile/update`:\n- Canon[\"alg\", \"iat\", \"tmb\", \"typ\", \"id\"] +\n- Option[\"display_name\", \"first_name\", \"last_name\", \"email\", \"address_1\",\n\"address_2\",\"phone_1\", \"phone_2\", \"city\",\"state\", \"zip\", \"country\"]\n@param {Profile} profile Profile for the user.\n@returns {Coze}\n@protected {normal=[canon,option]}\n */\nasync function ProfileUpdate(profile) {\n\tif (isEmpty(profile.id) || !Cyphrme.IsCyphrmeDigest(profile.id)) {\n\t\tCyphrme.Error(\"Profile is missing 'id'\");\n\t}\n\t// TODO Pass to Coze.Normal().\n\tlet pay = await GenPayStandard();\n\t// Required fields \n\tpay.typ = Typs.TypUserProfileUpdate;\n\tpay.id = profile.id;\n\n\tdelete profile.id;\n\tdelete profile.typ;\n\n\t// Optional fields\n\tfor (let key in profile) {\n\t\tpay[key] = profile[key];\n\t}\n\treturn GenCoze(pay);\n};\n\n/**\nProfilePictureUpdate\n@param {Blob} file\n@returns {Coze}\n@protected {normal=[canon,option]}\n */\nasync function ProfilePictureUpdate(file) {\n\tlet ext = File.GetFileExtension(file.name);\n\t// TODO svg's are not displaying in html for unknown reason. If that works,\n\t// support svg. Everything else with svg tested.\n\tif (![\"jpeg\", \"jpg\", \"png\", \"gif\"].includes(ext)) {\n\t\tCyphrme.Error(\"unsupported image type \" + ext);\n\t}\n\n\tif (isEmpty(Login.UAD) || !Cyphrme.IsCyphrmeDigest(Login.UAD)) {\n\t\tCyphrme.Error(\"User must be logged in.\");\n\t}\n\t// TODO Pass to Coze.Normal().\n\tlet pay = await GenPayStandard();\n\t// Required fields \n\tpay.typ = Typs.TypUserProfilePictureUpdate;\n\tpay.id = Login.UAD;\n\tpay.profile_picture = await Lib.HashFile(file); // Empty alg defaults to \"SHA-256\".;\n\treturn GenCoze(pay);\n};\n\n\n////////////////////////////////////////////////////////////////////////////////\n// Comment\n////////////////////////////////////////////////////////////////////////////////\n\n/**\ncommentCreate returns a required pay for the given comment typ. No optional\nfields in the returned CommentPay is populated.\n@param {string} typ\n@param {CommentPay} cp\n@param {boolean} edit=false\n@returns {Coze}\n */\nasync function commentCreate(typ, cp, edit) {\n\tlet pay = await GenPayStandard();\n\tpay.typ = typ;\n\tif (edit) {\n\t\tpay.id = cp.id;\n\t}\n\tpay.root = cp.root;\n\tdelete cp.root;\n\t// Optional fields. If 'parent' is set, comment is a reply. Replies have the\n\t// digest of the text being replied to (`rtd`).\n\tfor (let field in cp) {\n\t\tpay[field] = cp[field];\n\t}\n\treturn GenCoze(pay);\n};\n\n/**\nCommentCreate returns a Coze for creating a comment.\nNOTE: pay has all comment/review fields that are being signed over,\ndirectly inside of pay. Server unmarshaller puts it in it's correct form.\n@param {CommentPay} cp\n@returns {Coze}\n@protected {normal=[canon,option]}\n */\nasync function CommentCreate(cp) {\n\treturn commentCreate(Typs.TypCommentCreate, cp);\n};\n\n/**\nCommentUpdate returns a Coze for updating/editing a Comment/Review.\n@param {CommentPay} cp\n@returns {Coze}\n@protected {normal=[canon,option]}\n */\nasync function CommentUpdate(cp) {\n\treturn commentCreate(Typs.TypCommentUpdate, cp, true);\n};\n\n/**\nCreates a Coze for deleting a comment.\n@param {B64} id Comment ID\n@returns {Coze}\n@protected {normal=[only]}\n */\nasync function CommentDelete(id) {\n\treturn GenCoze({\n\t\ttyp: Typs.TypCommentDelete,\n\t\tid: id,\n\t});\n};\n\n\n////////////////////////////////////////////////////////////////////////////////\n// File/Image\n////////////////////////////////////////////////////////////////////////////////\n\n/**\nFileCreate returns a signed Coze action for creating a file (which may be an\nimage). Returns an signed Coze with these pay fields: \n{\"alg\", \"iat\", \"tmb\",\"typ\", \"id\",\"root\",\"ext\",\"file_name\", child_ac}\n@param {object} file File Object\n@param {B64} [root] Optional root if file is attached to something.\n@param {B64} [child_ac] Optional child_ac if root is a model's child.\n@returns {Coze}\n@protected {normal=[only]}\n */\nasync function FileCreate(file, root, child_ac) {\n\tlet pay = await GenPayStandard();\n\tpay.typ = Typs.TypFileCreate;\n\tpay.id = await Lib.HashFile(file, Login.CozeKey.alg)\n\tpay.ext = File.GetFileExtension(file.name)\n\tpay.file_name = file.name;\n\n\t// Only sign root if given.\n\tif (!isEmpty(root)) {\n\t\tpay.root = root;\n\t}\n\t// Only sign child_ac if given.\n\tif (!isEmpty(child_ac)) {\n\t\tpay.child_ac = child_ac;\n\t}\n\treturn GenCozeWithMeta(pay);\n};\n\n/**\nFileDelete returns a signed Coze action for deleting a file.\n@param {B64} czd File czd\n@returns {Coze}\n@protected {normal=[only]}\n */\nasync function FileDelete(czd) {\n\treturn GenCoze({\n\t\ttyp: Typs.TypFileDelete,\n\t\tid: czd\n\t});\n};\n\n// /**\n// * Creates a Coze for creating an image. `img` contains the digest of the image.\n// * img.root is the root subject, and the target subject. TODO Typedef.\n// * \n// * @param {object} img Image Object\n// * @returns {Coze}\n// * @protected {normal=[only]}\n// */\n// async function ImageCreate(img) {\n// \tlet pay = await GenPayStandard();\n// \tpay.typ = \"cyphr.me/ac/image/create\";\n// \tpay.id = img.id;\n// \tpay.ext = img.ext;\n// \tpay.file_name = img.file_name;\n// \tpay.root = img.root;\n\n// \treturn GenCozeWithMeta(pay);\n// };\n\n// /**\n// * Creates an action for deleting an image.\n// * \n// * @param {B64} czd Image czd\n// * @returns {Coze}\n// * @protected {normal=[only]}\n// */\n// async function ImageDelete(czd) {\n// \treturn GenCoze({\n// \t\tid: czd,\n// \t\ttyp: \"cyphr.me/image/delete\"\n// \t});\n// };", "\"use strict\"\n\nimport * as Cyphrme from './cyphrme.js'\nimport * as Cva from './cva.js'\n\nexport {\n\tBundleTypes,\n\n\t// NewBLS,\n\tBundleCVAsFromTree,\n\tBLSItemCount,\n\tBLSTopToString,\n\tDecrementBundle,\n\tBLSToGUIString,\n\n}\n\n/**\n@typedef {import ('../page/ac_gen_pj.js').PJ} PJ\n@typedef {import ('../lib/tree.js').Tree} Tree\n@typedef {import ('../lib/tree.js').BLS} BLS\n\n@typedef {string} Bundle e.g. \"item\", \"page\", or \"batch\" etc.\n*/\n\n// Bundle Types\nconst btItem = \"item\"\nconst btPage = \"page\"\nconst btBatch = \"batch\"\nconst btBox = \"box\"\nconst btPallet = \"pallet\"\nconst btRack = \"rack\"\nconst btWarehouse = \"warehouse\"\n\n// Enumerated and supported Bundle types. Also known as `bundle_type`.\nconst BundleTypes = [btItem, btPage, btBatch, btBox, btPallet, btRack, btWarehouse]\n\n/**\nBundleCVAsFromTree generates bundle CVAs recursively from tree. Bundles are\npopulated according to branch_levels in tree. E.g. a bundle with depth sizes:\n[1, 5, 10, 30] means 1 pallet, with 5 boxes each containing 10 pages of 30 items\n(labels), which is 56 different bundles.\n@param {Tree} tree\n@returns {BundleCVAs}\n*/\nasync function BundleCVAsFromTree(tree) {\n\t// console.log(\"BundleCVAsFromTree\", tree)\n\tlet BCVA = []\n\tif (isEmpty(tree.children)) {\n\t\treturn BCVA\n\t}\n\tlet numerator = 0\n\tlet denominator = tree.children.length\n\tfor (let child of tree.children) {\n\t\tnumerator++\n\t\tlet thing = {\n\t\t\tid: child.id,\n\t\t\tparent: tree.id,\n\t\t\tdeterm: true,\n\t\t\tbundle_type: BundleTypes[child.branch_level_sizes.length],\n\t\t\tnumer: numerator,\n\t\t\tdenom: denominator,\n\t\t\tn_childs: child.branches.length\n\t\t}\n\t\tif (!isEmpty(child.children)) { // children are empty on leaves.\n\t\t\tthing.n_childs = child.children.length\n\t\t}\n\t\tBCVA.push(await Cva.BundleCreate(thing))\n\t\tBCVA = BCVA.concat(await BundleCVAsFromTree(child)) // Recursive call.\n\t}\n\n\treturn BCVA\n}\n\n/**\ndecrementBundle decrements the Bundle Level to the next level down. Errors on\ninvalid bundle types. If the bottom level, \"item\", \"item\" is returned.\n@param {Bundle} bundle bundle type.\n@returns {Bundle}\n*/\nfunction DecrementBundle(bundle) {\n\tswitch (bundle) {\n\t\tdefault:\n\t\t\tCyphrme.Error(\"bundle is an undefined category: \" + bundle)\n\t\tcase btItem: // Last level (ACs, stickers, thing), do nothing.\n\t\t\tbreak\n\t\tcase btPage:\n\t\t\tbundle = btItem\n\t\t\tbreak\n\t\tcase btBatch:\n\t\t\tbundle = btPage\n\t\t\tbreak\n\t\tcase btBox:\n\t\t\tbundle = btBatch\n\t\t\tbreak\n\t\tcase btPallet:\n\t\t\tbundle = btBox\n\t\t\tbreak\n\t\tcase btRack:\n\t\t\tbundle = btPallet\n\t\t\tbreak\n\t\tcase btWarehouse:\n\t\t\tbundle = btRack\n\t}\n\treturn bundle\n}\n\n/**\nBLSTopToString converts the top brach_level_sizes to string, e.g. [1,1,3]\nconverts to \"batch\". \n@param {BLS} bls \n@param {Bundle}\n*/\nfunction BLSTopToString(bls) {\n\tlet level = bls.length - 1\n\tif (level >= BundleTypes.length) {\n\t\tCyphrme.Error(\"Unsupported batch depth.\")\n\t}\n\treturn BundleTypes[level]\n}\n\n/**\nBLSItemCount calculates how many total items in the entire tree is in a brach_level_sizes \n@param {BLS} bls \n@param {number}\n*/\nfunction BLSItemCount(bls) {\n\t// console.log(bls);\n\tlet sum = 0\n\tfor (let depth of bls) {\n\t\tif (sum === 0) {\n\t\t\tsum = depth\n\t\t\tcontinue\n\t\t}\n\t\tsum *= depth\n\t}\n\treturn sum\n}\n\n/**\nBLSItemCount calculates how many total items in the entire tree is in a brach_level_sizes \n@param {string} s Bracketless BLS e.g. `\"1,1,30\"`\n@param {BLS}\n*/\nfunction StringToBLS(s) {\n\treturn JSON.parse(\"[\" + s + \"]\")\n}\n\n/**\nBLSToGUIString returns the GUI representation of BLS.\n@param {BLS} bls BLS e.g. `[1,1,30]`\n@param {string} `1,1,30`\n*/\nfunction BLSToGUIString(bls) {\n\tlet s = JSON.stringify(bls)\n\treturn s.substring(1, s.length - 1)\n}", "\"use strict\";\n\nimport '../pkg/qrgen~fv=t0jhkAEE.min.js'; // Namespaced as 'qrgen'.\n\nexport {\n\tCreateQR\n};\n\n/**\nCreateQR creates a QR code and returns the QR in an HTMLDivElement.\nSee 'qr_div' in 'labels.css' for more.\n@param {string} qrText The text that is being put into the QR code.\n@returns {HTMLDivElement}\n*/\nasync function CreateQR(qrText) {\n\tvar qrdiv = document.createElement('div');\n\tqrdiv.classList.add(\"qr_div\");\n\tqrdiv.setAttribute('value', qrText);\n\n\t// Second parameter is boarder (Values 0-4).\n\tqrdiv.innerHTML = qrgen.QrCode.encodeText(qrText, qrgen.QrCode.Ecc.LOW).toSvgString(0);\n\treturn qrdiv;\n};\n\n", "\"use strict\"\n\n/**\nNote on label naming convention:\n\nIndustry standard for layout of pages are x,y, and for labels are y,x. We use\nx,y for the human readable names, and y,x for all other label size naming,\nincluding HTML IDs.\n\nPeriods, such as 1.625 are denoted with a p. e.g. 1p625. This is to avoid\npotential conflicts, such as `.` in scss files, potential file naming, etc.\nHuman readable formats use the period instead of p.\n */\n\nimport * as QR from \"./qr.js\";\n\nexport {\n\tDefaultLabelOptions,\n\tLabels,\n\n\tCreateLabelElement,\n\tCreateACLabel,\n\tCustomPrintCSS,\n};\n\n/**\nLabelPredefinedTemplateClass is the identifier for label sizes and styling.\nExample: `p_2p625x1` = Cyphr.me's standard 2.625\" x 1\" long labels.\n@typedef {string} LabelPredefinedTemplateClass\n */\n\n/**\nLabelOptions are used for global/stateful variables used for labels that are are\nheld in an object, and can be passed around to funcs and modules.\n\n- className: Label's class name for identifying stylesheets per page.\n- labelsPerPage: How many labels fit on a page with current dimensions.\n- labelBrandingLevel: Level of branding styling to use. See 'setLabelBranding in\n labels.js'.\n- acTitle: Text for displaying on the label. Defaults to empty string.\n- testACTitle: Bool for debugging that populates test title text on labels.\n- labelsGenerated: Current number of ACs imported for printing.\n- labelVertical: Bool for vertical label styling.\n- labelRotate90: Bool for rotating label 90 degrees.\n- labelCustomLogo: Bool for using custom logo in leu of title text.\n- labelCustomLogoImageLink: Image link for displaying custom logo.\n- startAtLabel: Position in current array of ACs to start at for printing.\n- startPosition: Starting position on the page for where to start printing.\n- isPrintViewInited: Whether or not the print view has been initialized.\n- noHeader: Removes header and footer with trademark and patent when set to\n true.\n- blankHeader: Preserves header formatting and spacing, but is blank when set to\n true.\n@typedef {object} LabelOptions\n@property {LabelPredefinedTemplateClass} className\n@property {number} labelsPerPage\n@property {string} labelBrandingLevel // TODO enum\n@property {string} acTitle\n@property {boolean} testACTitle\n@property {number} labelsGenerated\n@property {boolean} labelVertical\n@property {boolean} labelRotate90\n@property {boolean} labelCustomLogo\n@property {string} labelCustomLogoImageLink\n@property {number} startAtLabel\n@property {number} startPosition\n@property {boolean} isPrintViewInited\n@property {boolean} noHeader\n@property {boolean} blankHeader\n */\n\n/**\nCustomPrintParams hold the custom styling options for stickers/labels, as well\nas page styling.\n\nTODO support other measurements in the future, cm, pixels, etc.\n\nPages are split up by Top Margin, Bottom Margin, and Inner Page, which is\nbetween the top and bottom margin.\n\nNumbers are currently all in terms of inches.\n\nid: The id is the identifier for label templates that have predefined\nCustomPrintParams. The id is the same as the key in 'Labels' but needed because\nonce objects are passed, the key used to fetch the object from labels do not\ncarry.\n\n- pageHeight: Height of each page\n- pageWidth: Width of each page \n- pageMarginTop: Top margin of each page\n- pageInnerPadLeft: Left padding of inner page\n- labelHeight: Height of each label\n- labelWidth: Width of each label\n- pageMarginBottom: Bottom margin of each page\n- pageMarginTopPaddingTop: Page's top margin's top padding\n- pageMarginBottomPaddingTop: Page's bottom margin's top padding\n- labelContentsPaddingTop: Labels HR Div's top padding\n- labelContentsMarginLeft: Labels HR Div's left margin\n- labelMarginLeft: Left margin of each label\n- labelAuthTitleHeight: Authentic and title sections height\n- labelAuthTitlePaddingTop: Authentic and title top padding\n- labelLogoHeight: HR Div img element max height\n- labelTitleLogoHeight: AC Title img element max height (inside HR Div)\n- labelInnerPaddingTop: Inner div of label top padding\n- labelInnerPaddingLeft: Inner div of label left padding\n- labelQRSize: Label's QR size\n- humanReadableNames: Array of human readable measurements and names.\n e.g. ['1\" x 2\"', '1\" x 2-5/8\"']\n\n# Boolean options:\n- pageLandscape: Sets the page to landscape view \n- highlight: Highlights components of the print preview.\n- outline: Outlines components of the label in print preview.\n\n# Internal read only values used by labels module:\n- isCustom: If true, all fields are modifiable.\n- LabelOptions: Directly embedded label options object.\n@typedef {object} CustomPrintParams\n@property {string} id\n@property {number} pageHeight\n@property {number} pageWidth\n@property {number} pageMarginTop\n@property {number} pageInnerPadLeft\n@property {number} labelHeight\n@property {number} labelWidth\n@property {number} pageMarginBottom\n@property {number} pageMarginTopPaddingTop\n@property {number} pageMarginBottomPaddingTop\n@property {number} labelContentsPaddingTop\n@property {number} labelContentsMarginLeft\n@property {number} labelMarginLeft\n@property {number} labelAuthTitleHeight\n@property {number} labelAuthTitlePaddingTop\n@property {number} labelLogoHeight\n@property {number} labelTitleLogoHeight\n@property {number} labelInnerPaddingTop\n@property {number} labelInnerPaddingLeft\n@property {number} labelQRSize\n\n@property {Array} humanReadableNames\n\n// Bool Options\n@property {boolean} pageLandscape\n@property {boolean} highlight\n@property {boolean} outline\n\n// Internal read only values\n@property {boolean} isCustom\n\n// Directly embedded LabelOptions.\n@property {...LabelOptions}\n */\n\n/** @type {LabelOptions} */\nconst DefaultLabelOptions = {\n\t\"labelCustomLogo\": false,\n\t\"acTitle\": \"\",\n\t\"testACTitle\": false,\n\t\"labelVertical\": false,\n\t\"labelBrandingLevel\": \"LogoAuthBrand\",\n\t\"labelsGenerated\": 0,\n\t\"isPrintViewInited\": false,\n\t\"labelsPerPage\": 0,\n\t\"pageClass\": \"\",\n\t\"startAtLabel\": 0,\n\t\"startPosition\": 0,\n};\n\n/**\nCreateLabelElement accept an ID (Anti-Counterfeit's ID) and a full QR URL to\ncreate the inner contents of a label, and return the label div element.\n\nExample of how to call this function and append the label to a parent div on the\npage: `div.appendChild(await CreateLabel(id, qrURL, lblOpts));`\n@param {string} qrURL Full QR URL for the label's QR.\n@param {LabelOptions} [lblOpts] Label options object.\n@returns {HTMLElement} printLabel HTML print label element.\n@throws {error}\n */\nasync function CreateLabelElement(qrURL, lblOpts) {\n\tlet printLabel = document.querySelector('#LabelTemplate').content.cloneNode(true).querySelector('.printLabel');\n\tlet innerDiv = printLabel.querySelector('.inner');\n\tlet hrDiv = document.querySelector(\"#HRDiv\").content.cloneNode(true).querySelector('.hrDiv');\n\n\tif (isEmpty(lblOpts)) {\n\t\tlblOpts = DefaultLabelOptions;\n\t}\n\n\tsetLabelBranding(hrDiv, lblOpts);\n\n\t// QR for Long Labels are aligned to the left of the HR Div, and in the\n\t// middle of the HR Div for Vertical Labels.\n\tlet qr = await QR.CreateQR(qrURL);\n\tif (lblOpts.labelVertical) {\n\t\thrDiv.querySelector('.cyphrmeLogo').insertAdjacentHTML('afterEnd', qr.outerHTML);\n\t} else {\n\t\tinnerDiv.append(qr);\n\t}\n\n\tinnerDiv.append(hrDiv);\n\treturn printLabel;\n}\n\n/**\nCreateLabel accepts an ID (Anti-Counterfeit's ID) and generates a label to\nbe added to the given div passed in.\n@param {HTMLElement} parentDiv HTML parent div for appending label.\n@param {string} qrURL Full QR URL for the label's QR.\n@param {LabelPredefinedTemplateClass} [templ] Template for Label Size parameters.\n@param {LabelOptions} [lblOpts] Label options object.\n@returns {void}\n@throws {error}\n */\nasync function CreateACLabel(parentDiv, qrURL, templ, lblOpts) {\n\tlet template = Labels[templ];\n\tif (isEmpty(template)) {\n\t\ttemplate = Labels[\"p_1x2p625\"]; // Default\n\t}\n\t// console.debug(\"ParentDiv: \", parentDiv, \"QRURL: \", qrURL, \"Templ: \", templ, \"LblOpts: \", lblOpts, \"Template: \", template)\n\n\t// Custom does not have predefined template, and must be given.\n\tif (templ === 'Custom') {\n\t\ttemplate = lblOpts\n\t} else if (\"acTitle\" in lblOpts) {\n\t\ttemplate.acTitle = lblOpts.acTitle // For when custom label titles are specified.\n\t}\n\n\t// Pass custom class so that other styles apply to the page. Otherwise, if\n\t// passed empty, all styles are under the parent class 'customSize' and\n\t// only the last style would be applied to all.\n\tlet css = CreateCustomLabelCSS(template, \".\" + templ)\n\t// console.debug(css)\n\n\t// Set custom CSS in a style element on the current page.\n\tlet s = document.createElement('style')\n\ts.id = \"customStyle_\" + templ\n\ts.textContent = css\n\tdocument.querySelector('head').append(s)\n\n\tShow(parentDiv)\n\n\tparentDiv.classList.add(templ)\n\tparentDiv.append(await CreateLabelElement(qrURL, template))\n\t// parentDiv.append(await CreateLabelElement(qrURL, lblOpts))\n};\n\n/**\nsetLabelBranding accepts an HR Div element and sets the branding specific\nstyling, based on the selected branding level.\n\nEnumerate Sticker Brand Level:\n\nTODOs:\nLogo \nLogoAuth\nLogoAuthTitle\n\nLevel for which label branding style to use. We currently support 6 levels\nof branding:\n1.) LogoAuthBrand (Default): Cyphr.me Logo -> Authentic -> Custom Logo / Title\n2.) BrandAuthLogo: Custom Logo -> Authentic -> Cyphr.me Logo\n3.) BrandAuthIconTitle: Custom Logo -> Authentic + Cyphr.me Bolt -> Custom title\n4.) BrandTitle: Custom Logo -> Custom title\n5.) BrandLogo: Custom Logo -> Cyphr.me Logo\n6.) Brand: Custom Logo\n@param {HTMLElement} hrDiv Label's HR Div.\n@param {LabelOptions} lblOpts Label options object.\n@returns {void}\n */\nfunction setLabelBranding(hrDiv, lblOpts) {\n\t// console.debug(lblOpts)\n\tlet isTitleSet = false;\n\tlet customMainLogo = true;\n\n\tswitch (lblOpts.labelBrandingLevel) {\n\t\tdefault: // Default is `LogoAuthBrand`\n\t\tcase \"LogoAuthBrand\":\n\t\t\t// Replace title with custom logo if option checked, otherwise, proceed\n\t\t\t// as normal.\n\t\t\tif (lblOpts.labelCustomLogo) { // Logo instead of text\n\t\t\t\thrDiv.querySelector('.acTitle .titleLogo').src = lblOpts.labelCustomLogoImageLink;\n\t\t\t\tisTitleSet = true;\n\t\t\t}\n\t\t\tcustomMainLogo = false\n\t\t\tbreak\n\t\tcase \"BrandAuthLogo\":\n\t\t\tisTitleSet = true;\n\t\t\thrDiv.querySelector('.acTitle .titleLogo').src = '/assets/img/cyphrme_long_500x135.png'; // TODO enum link\n\t\t\tbreak\n\t\tcase \"BrandAuthIconTitle\":\n\t\t\tShow(hrDiv.querySelector('.authentic .bolt'));\n\t\t\tbreak\n\t\tcase \"BrandTitle\":\n\t\t\tHide(hrDiv.querySelector('.authentic'));\n\t\t\tif (lblOpts.labelCustomLogoImageLink === \"\") {\n\t\t\t\tlblOpts.labelCustomLogoImageLink = \"/assets/img/cyphrme_long_500x135.png\"\n\t\t\t}\n\t\t\tbreak\n\t\tcase \"BrandLogo\":\n\t\t\tHide(hrDiv.querySelector('.authentic'));\n\t\t\tisTitleSet = true\n\t\t\thrDiv.querySelector('.acTitle .titleLogo').src = '/assets/img/cyphrme_long_500x135.png';\n\t\t\tbreak\n\t\tcase \"Brand\":\n\t\t\tHide(hrDiv.querySelector('.authentic'));\n\t\t\tHide(hrDiv.querySelector('.acTitle'))\n\t\t\tif (lblOpts.labelCustomLogoImageLink === \"\") {\n\t\t\t\tlblOpts.labelCustomLogoImageLink = \"/assets/img/cyphrme_long_500x135.png\"\n\t\t\t}\n\t\t\tbreak\n\t}\n\n\tif (customMainLogo) {\n\t\thrDiv.querySelector('.cyphrmeLogo img').src = lblOpts.labelCustomLogoImageLink;\n\t}\n\n\tif (!isTitleSet) {\n\t\t// Pull the AC title\n\t\tif (lblOpts.testACTitle) { // Test text\n\t\t\thrDiv.querySelector('.acTitle').textContent = \"Cyphr.me Title\";\n\t\t} else { // Text\n\t\t\thrDiv.querySelector('.acTitle').textContent = lblOpts.acTitle;\n\t\t}\n\t}\n}\n\n\n/**\nCustomPrintCSS generates custom CSS for printing from fields in the custom print\nform and applies it to the page in a newly created style element.\n\nState grows when calling this function. If needing to clear, that must be done\nprior to this call. e.g. : document.querySelector('#customStyle').remove();\n@param {CustomPrintParams} cpp\n@returns {void}\n */\nfunction CustomPrintCSS(cpp, templ) {\n\t//// Generate custom CSS. Other styles in use can be found in 'labels.css'.\n\t// Precedence: Origin -> Page -> Margin Top -> Margin Bottom -> Page Inner\n\tlet css = CreateCustomPageCSS(cpp, templ) + CreateCustomLabelCSS(cpp, templ);\n\t// console.debug(css);\n\n\t//// Set custom CSS in a style element on the current page.\n\n\tlet s = document.createElement('style');\n\ts.id = \"customStyle\"\n\ts.textContent = css;\n\tdocument.querySelector('head').append(s);\n\n\t//// If needing to download CSS to a file.\n\t// Function to download data to a file.\n\t// Stolen from: https://stackoverflow.com/a/30832210/15147681\n\t// let download = function (data, filename, type) {\n\t// \tvar file = new Blob([data], {\n\t// \t\ttype: type\n\t// \t});\n\t// \tif (window.navigator.msSaveOrOpenBlob) // IE10+\n\t// \t\twindow.navigator.msSaveOrOpenBlob(file, filename);\n\t// \telse { // Others\n\t// \t\tvar a = document.createElement(\"a\"),\n\t// \t\t\turl = URL.createObjectURL(file);https://localhost:8081/api/v1/b/file/U04tb5U74_vVhevufEgH0XskNyGM2U49NV25DBHZ-Xg\n\t// \t\t\tdocument.body.removeChild(a);\n\t// \t\t\twindow.URL.revokeObjectURL(url);\n\t// \t\t}, 0);\n\t// \t}\n\t// }\n\t//\n\t// download(css, \"custom.css\", \"text/plain\");\n}\n\n/**\nCreateCustomPageCSS generates and returns custom CSS styles for elements that\nare used for page styling.\n\nAll whitespace/new lines/tabs inside of `` marks is relevant for CSS output\nand should not be removed.\n\n`templ` must be passed with the prepended '.', because it is a CSS selector\nstring for the label class.\n@param {CustomPrintParams} cpp\n@param {LabelPredefinedTemplateClass} [templ]\n@returns {string} css\n */\nfunction CreateCustomPageCSS(cpp, templ) {\n\tif (isEmpty(templ)) {\n\t\ttempl = '.customSize';\n\t}\n\t// Margin bottom is implicit if not explicitly given. Margin bottom with an\n\t// input of 0 is interpreted as empty from URL form. If wanting the default\n\t// margin bottom, the input field should be blank. If wanting to set the\n\t// margin bottom to 0, the value must be '0.0'.\n\tlet margBottom = `\\n`;\n\tif (!isEmpty(cpp.pageMarginBottom) || cpp.pageMarginBottom >= 0) {\n\t\t// margBottom += `height:` + cpp.pageMarginBottom + `in;\\n`;\n\t\tif (cpp.pageMarginBottom === 0) {\n\t\t\tmargBottom += `padding: 0;\\n`\n\t\t}\n\t}\n\n\t// Rotate page 90 degrees for landscape view.\n\tlet landscape = ``;\n\tif (cpp.pageLandscape) {\n\t\tlandscape = `\n.page.flip {\ntransform: rotate(-90deg) translateX(-` + cpp.pageWidth + `in);\ntransform-origin: top left;\n}\n\n`;\n\t}\n\n\t//// Generate custom CSS. Other styles in use can be found in 'labels.css'.\n\t// Precedence: Origin -> Page -> Margin Top -> Margin Bottom -> Page Inner\n\tlet css = `\n.page {\n\theight:` + cpp.pageHeight + `in;\n\twidth:` + cpp.pageWidth + `in;\n}\n\n.margin {\n\theight:` + cpp.pageMarginTop + `in;\n}\n\n.marginTop {\npadding-top:` + cpp.pageMarginTopPaddingTop + `in;\n}\n\n.marginBottom {\npadding-top:` + cpp.pageMarginBottomPaddingTop + `in;` +\n\t\tmargBottom +\n\t\t`}\n\n` + templ + ` > .pageInner {\npadding-left:` + cpp.pageInnerPaddingLeft + `in;\n}\n` + landscape;\n\n\treturn css;\n}\n\n\n/**\nCreateLabelCSS generates and returns custom label CSS for the given custom\nprint params object.\n\nAll whitespace/new lines/tabs inside of `` marks is relevant for CSS output\nand should not be removed.\n@param {CustomPrintParams} cpp\n@param {LabelPredefinedTemplateClass} [templ]\n@returns {string} css\n */\nfunction CreateCustomLabelCSS(cpp, templ) {\n\tif (isEmpty(templ)) {\n\t\ttempl = '.customSize';\n\t}\n\t// Authentic and AC Title Font Size is calculated based upon the given \n\t// label's Authentic Height. The font is 1/8th smaller than the height.\n\t// If no height is given, the default font size is 10 pixels.\n\tlet authenticAndTitleFontSize = cpp.labelAuthTitleHeight * .875;\n\tif (isEmpty(cpp.labelAuthTitleHeight) || cpp.labelAuthTitleHeight <= 0) {\n\t\tauthenticAndTitleFontSize = 0.1; // ~ 10px\n\t}\n\n\t// Vertical\n\tlet qrFloat = \"left;\";\n\tif (cpp.labelVertical) {\n\t\tqrFloat = `none;\ndisplay: inline-block;`;\n\t}\n\n\t// Rotate clockwise 90 degrees.\n\t// A 90 degree rotation always require a translation.\n\tlet r90 = ``;\n\tif (cpp.labelRotate90) {\n\t\t// Flip width and height for intermediate before rotate.\n\t\tr90 = templ + ` .printLabel .r90 {\ntransform: rotate(.25turn) translate(0in, -` + cpp.labelWidth + `in);\ntransform-origin: top left;\n}\n\n` + templ + ` .printLabel .inner {\nwidth:` + cpp.labelHeight + `in;\nheight:` + cpp.labelWidth + `in;\n}\n\n`;\n\t}\n\n\t// Generate custom label CSS. Other styles in use can be found in 'labels.css'.\n\tlet css = templ + ` .hrDiv {\npadding-top:` + cpp.labelContentsPaddingTop + `in;\nmargin-left:` + cpp.labelContentsMarginLeft + `in;\n}\n\n` + templ + ` .printLabel {\nwidth:` + cpp.labelWidth + `in;\nheight:` + cpp.labelHeight + `in;\nmargin-left:` + cpp.labelMarginLeft + `in;\n}\n\n` + templ + ` .printLabel .hrDiv .cyphrmeLogo img {\nmax-height:` + cpp.labelLogoHeight + `in;\n}\n\n` + templ + ` .printLabel .hrDiv .acTitle img {\nmax-height:` + cpp.labelTitleLogoHeight + `in;\n}\n\n` + templ + ` .printLabel .inner {\npadding-top:` + cpp.labelInnerPaddingTop + `in;\npadding-left:` + cpp.labelInnerPaddingLeft + `in;\n}\n\n` + templ + ` .printLabel .qr_div {\nwidth:` + cpp.labelQRSize + `in;\nheight:` + cpp.labelQRSize + `in;\nfloat:` + qrFloat + `\n}\n\n` + templ + ` .printLabel .authentic {\npadding-top:` + cpp.labelAuthTitlePaddingTop + `in;\n}\n\n` + templ + ` .printLabel .authentic span {\nfont-size:` + authenticAndTitleFontSize + `in;\n}\n\n` + templ + ` .printLabel .authentic img {\nheight:` + cpp.labelAuthTitleHeight + `in;\n}\n\n` + templ + ` .printLabel .acTitle {\npadding-top:` + cpp.labelAuthTitlePaddingTop + `in;\nfont-size:` + authenticAndTitleFontSize + `in;\n}` + r90;\n\n\treturn css;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Enums\n////////////////////////////////////////////////////////////////////////////////\n\n// Standard US Letter page size.\nlet standardUSLetter = {\n\tpageHeight: 11,\n\tpageWidth: 8.5,\n};\n\n// Standard roll size.\nlet standardRoll = {\n\tpageHeight: 110,\n\tpageWidth: 2.5,\n};\n\n\n// Standard 1/2\" margins for top and bottom with batch numbers.\nlet standardCyphrmeMargins = {\n\t\"pageMarginTop\": .5,\n\t\"pageMarginBottom\": 0,\n\t\"pageMarginTopPaddingTop\": .22,\n\t\"pageMarginBottomPaddingTop\": .03,\n}\n\n// Standard level 1 branding.\nlet cyphrmeBrandingLvl1 = {\n\t\"labelCustomLogoImageLink\": \"\",\n\t\"labelBrandingLevel\": \"LogoAuthBrand\",\n}\n\n// For labels with no checkbox options checked.\nlet noCheckboxOpts = {\n\t\"pageLandscape\": false,\n\t\"labelVertical\": false,\n\t\"labelRotate90\": false,\n\t\"labelCustomLogo\": false,\n\t\"noHeader\": false,\n\t\"blankHeader\": false,\n}\n\n// For debugging and print preview styling that does not get printed.\nlet noDebugFields = {\n\t\"highlight\": false,\n\t\"outline\": false,\n\t\"startAtLabel\": 0,\n\t\"startPosition\": 0,\n}\n\n//// Label Sizes\n\n/**\n`className` Must only be set if the className differs from the 'id'.\n @type {{LabelPredefinedTemplateClass:CustomPrintParams}}\n */\nconst Labels = {\n\t// Custom preserves and enables all fields.\n\t\"Custom\": {\n\t\t\"id\": \"Custom\",\n\t\t\"className\": \"customSize\",\n\t},\n\t// Blank clears and enables all fields and sets the current label size option\n\t// to custom.\n\t\"Blank\": {\n\t\t\"id\": \"Blank\",\n\t\t\"className\": \"customSize\",\n\t\t\"pageHeight\": 0,\n\t\t\"pageWidth\": 0,\n\t\t\"pageMarginTop\": 0,\n\t\t\"pageMarginBottom\": 0,\n\t\t\"pageMarginTopPaddingTop\": 0,\n\t\t\"pageMarginBottomPaddingTop\": 0,\n\t\t\"pageInnerPaddingLeft\": 0,\n\t\t\"labelHeight\": 0,\n\t\t\"labelWidth\": 0,\n\n\t\t...noDebugFields,\n\t\t// Debugging values\n\t\t...noDebugFields,\n\t\t// Checkbox values\n\t\t...noCheckboxOpts,\n\t\t// Title Logo options\n\t\t...cyphrmeBrandingLvl1,\n\n\t\t// Set non required fields\n\t\t\"labelContentsPaddingTop\": 0,\n\t\t\"labelContentsMarginLeft\": 0,\n\t\t\"labelMarginLeft\": 0,\n\t\t\"labelAuthTitleHeight\": 0,\n\t\t\"labelAuthTitlePaddingTop\": 0,\n\t\t\"labelLogoHeight\": 0,\n\t\t\"labelTitleLogoHeight\": 0,\n\t\t\"labelInnerPaddingTop\": 0,\n\t\t\"labelInnerPaddingLeft\": 0,\n\t\t\"labelQRSize\": 1,\n\t\t\"labelsPerPage\": 0,\n\t},\n\t// Standard size - 1\" x 2.625\"\n\t\"p_1x2p625\": {\n\t\t\"id\": \"p_1x2p625\",\n\t\t\"humanReadableNames\": ['1\" x 2.625\"', '1\" x 2-5/8\"'],\n\t\t...standardUSLetter,\n\t\t...standardCyphrmeMargins,\n\n\t\t\"pageInnerPaddingLeft\": .0625, // Don't round\n\t\t\"labelHeight\": 1,\n\t\t\"labelWidth\": 2.625,\n\n\t\t// Debugging values\n\t\t...noDebugFields,\n\t\t// Checkbox values\n\t\t...noCheckboxOpts,\n\t\t// Title Logo options\n\t\t...cyphrmeBrandingLvl1,\n\n\t\t// Set non required fields\n\t\t\"labelContentsPaddingTop\": .03,\n\t\t\"labelContentsMarginLeft\": .1,\n\t\t\"labelMarginLeft\": .125,\n\t\t\"labelAuthTitleHeight\": 0.15,\n\t\t\"labelAuthTitlePaddingTop\": .03,\n\t\t\"labelLogoHeight\": 0.37,\n\t\t\"labelTitleLogoHeight\": .25,\n\t\t\"labelInnerPaddingTop\": .12,\n\t\t\"labelInnerPaddingLeft\": .165,\n\t\t\"labelQRSize\": .78,\n\t\t\"labelsPerPage\": 30,\n\t},\n\t// 1\" x 2\"\n\t\"p_1x2\": {\n\t\t\"id\": \"p_1x2\",\n\t\t\"humanReadableNames\": ['1\" x 2\"'],\n\t\t...standardUSLetter,\n\t\t...standardCyphrmeMargins,\n\n\t\t\"pageInnerPaddingLeft\": 0.25,\n\t\t\"labelHeight\": 1,\n\t\t\"labelWidth\": 2,\n\n\t\t// Debugging values\n\t\t...noDebugFields,\n\n\t\t// Checkbox values\n\t\t...noCheckboxOpts,\n\n\t\t// Title Logo options\n\t\t...cyphrmeBrandingLvl1,\n\n\t\t// Set non required fields\n\t\t\"labelContentsPaddingTop\": 0,\n\t\t\"labelContentsMarginLeft\": 0,\n\t\t\"labelMarginLeft\": 0,\n\t\t\"labelAuthTitleHeight\": 0.17,\n\t\t\"labelAuthTitlePaddingTop\": .03,\n\t\t\"labelLogoHeight\": 0.3,\n\t\t\"labelTitleLogoHeight\": .25,\n\t\t\"labelInnerPaddingTop\": .125,\n\t\t\"labelInnerPaddingLeft\": .09,\n\t\t\"labelQRSize\": .78,\n\t\t\"labelsPerPage\": 40,\n\t},\n\t// 1/2\" x 1-3/4\"\n\t\"p_05x1p75\": {\n\t\t\"id\": \"p_05x1p75\",\n\t\t...standardUSLetter,\n\t\t...standardCyphrmeMargins,\n\n\t\t\"pageInnerPaddingLeft\": 0,\n\t\t\"labelHeight\": .5,\n\t\t\"labelWidth\": 1.75,\n\n\t\t// Debugging values\n\t\t...noDebugFields,\n\n\t\t// Checkbox values\n\t\t...noCheckboxOpts,\n\n\t\t// Title Logo options\n\t\t...cyphrmeBrandingLvl1,\n\n\t\t// Set non required fields\n\t\t\"labelContentsPaddingTop\": 0,\n\t\t\"labelContentsMarginLeft\": 0,\n\t\t\"labelMarginLeft\": 0.3,\n\t\t\"labelAuthTitleHeight\": 0.09,\n\t\t\"labelAuthTitlePaddingTop\": .01,\n\t\t\"labelLogoHeight\": .22,\n\t\t\"labelTitleLogoHeight\": .15,\n\t\t\"labelInnerPaddingTop\": .04,\n\t\t\"labelInnerPaddingLeft\": .06,\n\t\t\"labelQRSize\": .4,\n\t\t\"labelsPerPage\": 80,\n\t},\n\n\t// 1\" x 1\"\n\t\"p_1x1\": {\n\t\t\"id\": \"p_1x1\",\n\t\t\"humanReadableNames\": ['1\" x 1\"'],\n\t\t...standardUSLetter,\n\t\t...standardCyphrmeMargins,\n\n\t\t\"pageInnerPaddingLeft\": 0.25,\n\t\t\"labelHeight\": 1,\n\t\t\"labelWidth\": 1,\n\n\t\t// Debugging values\n\t\t...noDebugFields,\n\n\t\t// Checkbox values\n\t\t\"pageLandscape\": false,\n\t\t\"labelVertical\": true,\n\t\t\"labelRotate90\": false,\n\t\t\"labelCustomLogo\": false,\n\t\t\"noHeader\": false,\n\t\t\"blankHeader\": false,\n\t\t// Title Logo options\n\t\t...cyphrmeBrandingLvl1,\n\n\t\t// Set non required fields\n\t\t\"labelContentsPaddingTop\": 0,\n\t\t\"labelContentsMarginLeft\": 0,\n\t\t\"labelMarginLeft\": 0,\n\t\t\"labelAuthTitleHeight\": 0.1,\n\t\t\"labelAuthTitlePaddingTop\": .02,\n\t\t\"labelLogoHeight\": .23,\n\t\t\"labelTitleLogoHeight\": .15,\n\t\t\"labelInnerPaddingTop\": .05,\n\t\t\"labelInnerPaddingLeft\": .03,\n\t\t\"labelQRSize\": .43,\n\t\t\"labelsPerPage\": 80,\n\t},\n\n\t// 1\" x 2\" vertical\n\t\"p_1x2_vertical\": {\n\t\t\"id\": \"p_1x2_vertical\",\n\t\t\"className\": \"customSize\",\n\t\t\"humanReadableNames\": ['1\" x 2\" vertical'],\n\t\t...standardUSLetter,\n\t\t...standardCyphrmeMargins,\n\n\t\t\"pageInnerPaddingLeft\": 0.25,\n\t\t\"labelHeight\": 1,\n\t\t\"labelWidth\": 2,\n\n\t\t// Debugging values\n\t\t...noDebugFields,\n\n\t\t// Checkbox values\n\t\t\"pageLandscape\": true,\n\t\t\"labelVertical\": true,\n\t\t\"labelRotate90\": true,\n\t\t\"labelCustomLogo\": false,\n\t\t\"noHeader\": false,\n\t\t\"blankHeader\": false,\n\n\t\t// Title Logo options\n\t\t...cyphrmeBrandingLvl1,\n\n\t\t// Set non required fields\n\t\t\"labelContentsPaddingTop\": 0,\n\t\t\"labelContentsMarginLeft\": 0,\n\t\t\"labelMarginLeft\": 0,\n\t\t\"labelAuthTitleHeight\": 0.15,\n\t\t\"labelAuthTitlePaddingTop\": .03,\n\t\t\"labelLogoHeight\": .25,\n\t\t\"labelTitleLogoHeight\": .15,\n\t\t\"labelInnerPaddingTop\": .2,\n\t\t\"labelInnerPaddingLeft\": 0,\n\t\t\"labelQRSize\": .78,\n\t\t\"labelsPerPage\": 40,\n\n\t\t// Set internal fields\n\t\t//\"isCustom\": true,\n\t},\n\n\t// Standard size - 1\" x 2.625\" w/ level 2 branding example.\n\t\"p_1x2p625_branded\": {\n\t\t\"id\": \"p_1x2p625_branded\",\n\t\t\"className\": \"customSize\",\n\t\t\"humanReadableNames\": ['1\" x 2.625\" branded'],\n\t\t...standardUSLetter,\n\t\t...standardCyphrmeMargins,\n\n\t\t\"pageInnerPaddingLeft\": .0625, // Don't round\n\t\t\"labelHeight\": 1,\n\t\t\"labelWidth\": 2.625,\n\n\t\t// Debugging values\n\t\t...noDebugFields,\n\t\t// Checkbox values\n\t\t\"pageLandscape\": false,\n\t\t\"labelVertical\": false,\n\t\t\"labelRotate90\": false,\n\t\t\"labelCustomLogo\": true,\n\t\t\"noHeader\": false,\n\t\t\"blankHeader\": false,\n\t\t// Title Logo options\n\t\t\"labelCustomLogoImageLink\": \"/assets/img/tankmatez_long.jpg\",\n\t\t\"labelBrandingLevel\": \"BrandAuthLogo\",\n\n\t\t// Set non required fields\n\t\t\"labelContentsPaddingTop\": .03,\n\t\t\"labelContentsMarginLeft\": .1,\n\t\t\"labelMarginLeft\": .125,\n\t\t\"labelAuthTitleHeight\": 0.15,\n\t\t\"labelAuthTitlePaddingTop\": .03,\n\t\t\"labelLogoHeight\": 0.28,\n\t\t\"labelTitleLogoHeight\": .2,\n\t\t\"labelInnerPaddingTop\": .125,\n\t\t\"labelInnerPaddingLeft\": .16,\n\t\t\"labelQRSize\": .78,\n\t\t\"labelsPerPage\": 30,\n\n\t\t// Set internal fields\n\t\t//\"isCustom\": true,\n\t},\n\t//////////////////////////////////////////////////////////////////////////////\n\t// Rolls\n\t//////////////////////////////////////////////////////////////////////////////\n\t// 1\" x 2\"\n\t\"p_1x2_roll\": {\n\t\t\"id\": \"p_1x2_roll\",\n\t\t\"humanReadableNames\": ['1\" x 2\" roll'],\n\t\t...standardRoll,\n\n\t\t\"pageMarginTop\": 0.25,\n\t\t\"pageMarginBottom\": 0,\n\t\t\"pageMarginTopPaddingTop\": 0,\n\t\t\"pageMarginBottomPaddingTop\": 0,\n\n\t\t\"pageInnerPaddingLeft\": 0.25,\n\t\t\"labelHeight\": 1,\n\t\t\"labelWidth\": 2,\n\n\t\t// Debugging values\n\t\t...noDebugFields,\n\n\t\t// Checkbox values\n\t\t\"pageLandscape\": false,\n\t\t\"labelVertical\": false,\n\t\t\"labelRotate90\": false,\n\t\t\"labelCustomLogo\": false,\n\t\t\"noHeader\": false,\n\t\t\"blankHeader\": true,\n\n\t\t// Title Logo options\n\t\t...cyphrmeBrandingLvl1,\n\n\t\t// Set non required fields\n\t\t\"labelContentsPaddingTop\": 0.04,\n\t\t\"labelContentsMarginLeft\": 0,\n\t\t\"labelMarginLeft\": 0,\n\t\t\"labelAuthTitleHeight\": 0.17,\n\t\t\"labelAuthTitlePaddingTop\": .03,\n\t\t\"labelLogoHeight\": 0.28,\n\t\t\"labelTitleLogoHeight\": .2,\n\t\t\"labelInnerPaddingTop\": 0.1,\n\t\t\"labelInnerPaddingLeft\": .09,\n\t\t\"labelQRSize\": .78,\n\t\t\"labelsPerPage\": 110,\n\t},\n\n};", "\"use strict\";\n\nimport * as Cyphrme from './cyphrme.js';\nimport * as Ajax from './ajax.js';\n\nexport {\n\tDeleteTableRow,\n\tInitRowCheckbox,\n\tInitTableButtonsToggle,\n\tNormalizeCheckBoxOptionsRecordMap,\n\tSetCheckBoxModals,\n\tSetModalPreview,\n}\n\n/**\n@typedef {import('../../../pkg/cozejs/typedef.js').B64} B64\n\nA CheckBoxOptions is an object that holds various components that are useful for\ntables that support checkbox options.\n\ncheckedList: Array of the records that have been checked.\n\ndeleteFunc: Function for setting on the modal's delete button.\n\nsupportDelete: Whether or not the checkbox options supports deletions.\n\ndeleteModal: Bootstrap modal that preview's records for deletion, and holds the\ndeletion button/function.\n\nextraBtns: Array of HTML Button Elements that can be appended to the table.\n.queryClass must be set on the button, which should be the class for querying\nthe element, prepended with a '.'.\n\nmodalFunc: Array of functions for initializing Bootstrap modals that shows a\npreview list of records being manipulated. The function must perform logic based\non the CheckBoxOptions object.\n\nrecordMap: Map holds the record/row ID as key, and the entirety of the record as\nthe value. A human readable field 'rowName' is added for expressing details\nabout the row, if applicable (e.g. 'kid' for keys, 'title' for ACs, etc.). A\nsecond field 'rowID' is added for special cases in which the record's id is not\nneeded, but another field, such as digest for files. Otherwise, this field\nshould always be set as the record/row id.\n@typedef {object} CheckBoxOptions \n@property {B64[]} checkedList\n@property {function} deleteFunc\n@property {boolean} supportDelete\n@property {Modal} deleteModal\n@property {Element[]} extraBtns\n@property {function[]} modalFunc\n@property {object} recordMap\n*/\n\n/**@type {NodeList} */\nvar delBtns;\n\n\n/**\nInitTableButtonsToggle sets the event listeners for toggling elements\nvisibility.\n@param {CheckBoxOptions} opts CheckBoxOptions for the page.\n@returns {void}\n */\nfunction InitTableButtonsToggle(opts) {\n\tif (opts.supportDelete) {\n\t\tdelBtns = document.querySelectorAll('.table_delete_btn');\n\t}\n\tif (!isEmpty(opts.extraBtns)) {\n\t\tdocument.querySelectorAll('.table_buttons').forEach(function (item) {\n\t\t\t// Must create a new element for each append to the page. cloneNode, append,\n\t\t\t// prepend, appendChild, etc. all act like cut and paste, and not copy paste.\n\t\t\tfor (let b of opts.extraBtns) {\n\t\t\t\tlet btn = b.cloneNode(true);\n\t\t\t\tShow(btn);\n\t\t\t\tbtn.addEventListener(b.event, b.event_listener);\n\t\t\t\titem.appendChild(btn)\n\t\t\t}\n\t\t});\n\t\t// console.debug(opts);\n\t}\n}\n\n\n/**\nSetCheckBoxModals instantiates the given modals, and set the event listeners.\nThe delete modal is always on a given table, but only activated when\n'CheckBoxOptions.supportDelete' is set to true. All modal instances other than\ndelete should be in 'CheckBoxOptions.modals'.\n@param {CheckBoxOptions} opts CheckBoxOptions for holding the modal instances.\n@returns {void}\n */\nfunction SetCheckBoxModals(opts) {\n\tif (opts.supportDelete) {\n\t\tsetDeleteModal(opts);\n\t}\n\tif (!isEmpty(opts.modals)) {\n\t\topts.modals.forEach((item) => item(opts));\n\t}\n}\n\n\n/**\nsetDeleteModal sets the delete fields on a CheckBoxOptions on the current page.\nThe event listeners are also set in this function, that enables the deletions\npreview modal, as well as setting the delete button's function that is on the\nmodal.\n@param {CheckBoxOptions} opts Holds the relevant data for setting the event listeners for checkbox deletions.\n@returns {void}\n */\nfunction setDeleteModal(opts) {\n\tlet dme = document.getElementById('deleteModal');\n\tlet deleteModal = new bootstrap.Modal(dme, {\n\t\tkeyboard: true\n\t});\n\n\tdme.querySelector('#deleteModalBtn').addEventListener('click', () => opts.deleteFunc());\n\n\tdocument.querySelectorAll('.table_delete_btn').\n\tforEach((item) => item.addEventListener('click', () => SetModalPreview(opts, deleteModal, \"deleteList\")));\n}\n\n\n/**\nSetModalPreview sets the modal-body list items on a model that supports\npreviews for manipulations to records in a table that supports checkboxes.\n@param {CheckBoxOptions} opts CheckboxOptions for holding recordMap.\n@param {HTMLElement} bsModalInstance Modal being manipulated and toggled.\n@param {string} divListID Element ID of the div containing the modification list.\n@returns {void}\n */\nasync function SetModalPreview(opts, bsModalInstance, divListID) {\n\tlet list = document.getElementById(divListID);\n\tlist.innerHTML = '';\n\t// Make a dummy div to append to before triggering the canvas append (which \n\t// is much less efficient).\n\tlet items = document.createElement('div');\n\tfor (let r of opts.checkedList) {\n\t\tlet item = document.createElement('li');\n\t\titem.classList.add(\"list-group-item\");\n\t\tlet name = opts.recordMap[r].rowTitle;\n\t\tlet id = opts.recordMap[r].rowID;\n\n\t\titem.textContent = id.substring(0, 6) + '...' + ' | ' + name;\n\t\titems.append(item);\n\t}\n\tlist.append(items); // triggers canvas draw\n\tbsModalInstance.toggle();\n}\n\n\n/**\nDeleteTableRow sets new GUI state for the table from server response.\nThis only works for endpoints that return parsd.obj as an array of IDs.\n@param {object} response Server's JSON Success response.\n@param {CheckBoxOptions} checkBoxOpts Current page's CheckBoxOptions State/Object.\n@returns {void}\n */\nfunction DeleteTableRow(parsd, checkBoxOpts) {\n\tif (isEmpty(parsd) || isEmpty(parsd.obj)) {\n\t\treturn\n\t}\n\tfor (let i = 0; i < parsd.obj.length; i++) {\n\t\tdocument.getElementById(parsd.obj[i]).remove();\n\t\tdelete checkBoxOpts.recordMap[parsd.obj[i]];\n\t}\n\tCyphrme.Notification(\"Request was sent successfully\", 'success');\n}\n\n/**\nTODO finish param typedef NormalizeCheckBoxOptionsRecordMap sets the recordMap\non a CheckBoxOptions. The key is the record/row id, with the value being the\nentirety of the record, with additional reserved field names added for\napplication normalization. Fields include:\n- rowID: The record/row's id.\n- rowName: The record/row's human readable name (if applicable).\n\nAll normalized/needed fields for CheckBoxOptions in the future should live here.\n@param {CheckBoxOptions} opts Holds the reference for the page's CheckBoxOptions.\n@returns {void}\n */\nasync function NormalizeCheckBoxOptionsRecordMap(opts, record, rowID, rowTitle) {\n\topts.recordMap[record.id] = {\n\t\t...record\n\t};\n\topts.recordMap[record.id].rowID = rowID; // Helpful for images, since digest is displayed in row\n\topts.recordMap[record.id].rowTitle = rowTitle; // Human Readable\n}\n\n\n/**\nInitRowCheckbox is for handling events on the HTML buttons for when a checkbox\non a row is changed. Enables/disables the button, depending on the checked list,\nand the supported flags. The delete button is supported across all tables, but\nis only enabled if CheckBoxOptions.supportDelete is set to true.\n@param {B64} id Table Row ID. Should also be the czd of the row record.\n@param {CheckBoxOptions} opts The Checkbox Options.\n@param {function} [f] Optional enable function that must have a signature of (CheckBoxOptions).\n@returns {void}\n*/\nfunction InitRowCheckbox(id, opts, f) {\n\tlet input = document.getElementById(id).querySelector('input');\n\tinput.disabled = false;\n\tShow(input);\n\n\t// console.debug(opts);\n\tinput.addEventListener('change', function (item) {\n\t\tif (item.target.checked) {\n\t\t\tif (!opts.checkedList.includes(id)) {\n\t\t\t\topts.checkedList.push(id);\n\t\t\t}\n\t\t\t// If table has delete button, enable\n\t\t\tif (opts.supportDelete) {\n\t\t\t\tfor (let btn of delBtns) {\n\t\t\t\t\tEnable(btn)\n\t\t\t\t\tShow(btn)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!isEmpty(opts.extraBtns)) {\n\t\t\t\topts.extraBtns.forEach((item) => document.querySelectorAll(item.queryClass).forEach((item) => item.disabled = false));\n\t\t\t}\n\t\t} else {\n\t\t\tlet n = opts.checkedList.indexOf(id);\n\t\t\topts.checkedList.splice(n, 1);\n\t\t\tif (opts.checkedList.length <= 0) {\n\t\t\t\t// If table has delete button, disable\n\t\t\t\tif (opts.supportDelete) {\n\t\t\t\t\tfor (let btn of delBtns) {\n\t\t\t\t\t\tDisable(btn)\n\t\t\t\t\t\tHide(btn)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!isEmpty(opts.extraBtns)) {\n\t\t\t\t\topts.extraBtns.forEach((item) => document.querySelectorAll(item.queryClass).forEach((item) => item.disabled = true));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Invoke optional enable/disable function\n\t\tif (!isEmpty(f)) {\n\t\t\tf(opts);\n\t\t}\n\n\t});\n}"], "mappings": "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", "names": ["Lib", "OnLoads", "Site", "ApiURI", "BApiURI", "CyphrmeLogoLong", "MaxFileSize", "QRCodeURL", "API", "Page", "AllPagesReady", "AddOnload", "OnloadLast", "i", "OnLoads", "RemoveOnload", "func", "AddOnloadFirst", "JumpToAnchor", "setCozeJSONLink", "JSONPretty", "item", "Page", "LogLevel", "SetLogLevel", "int", "Log", "msg", "trace", "log", "intLevel", "Notification", "message", "level", "mainAlert", "bsAlert", "Error", "error", "UTKtoHR", "utk", "trim", "UnixToHR", "timestamp", "options", "IsCyphrmeDigest", "string", "ParseSerializedCozeKey", "splits", "CozeKey", "IsHex", "id", "IsBASE37", "json", "hash", "urlHash", "p", "anchor", "element", "url", "Collapse", "toggleElement", "visibleElement", "callback", "icon", "Fetch", "url", "jsonResErrorHandler", "getFetch", "FetchHTML", "res", "Error", "FetchPost", "data", "postFetch", "parsd", "e", "Batcher", "batchSize", "values", "endpoint", "callback", "aSink", "f", "formData", "response", "error", "i", "batch", "InitPagRec", "res", "item", "setPaginateDropdown", "SetPaginationArrows", "e", "Error", "paginate", "div", "limit", "a", "pagCopy", "urlParam", "stringToType", "canon", "GenURLFromPaginate", "s", "i", "pagrec", "copy", "urlp", "GetCurrentURLParams", "Site", "params", "leftPF", "rightPF", "page", "ord", "order", "type", "current", "depth", "keys", "urlParams", "GenTable", "headers", "th", "h", "AddTableRow", "TableElement", "row", "id", "createTableRow", "AddTableRowFirst", "tr", "td", "Lib", "PR", "initedFormOptions", "CommentReviewModule", "pr", "cmnt", "CreateReviews", "UAD", "subed", "rec", "btn", "Page", "revs", "div", "rev", "createReview", "appendChildren", "comment", "depth", "link", "current", "GetCurrentURLParams", "i", "child", "createComment", "card", "ce", "createExtraAppend", "Element", "Name", "Value", "s", "c", "replyBtn", "editBtn", "deleteBtn", "options", "createdDate", "updatedDate", "ownerLink", "text", "Collapse", "commentReplyOrEdit", "commentDelete", "edit", "rd", "change", "setReplyFunc", "replyDiv", "reviewObj", "editableReview", "submitFunc", "event", "setReviewPay", "SubmitComment", "textArea", "ratingElem", "purchasedElem", "purchaseLocationElem", "ctfElem", "templ", "review", "getStarsFromRating", "stars", "star", "SetRatingStar", "textarea", "CreateSubmitReviewForm", "reviewForm", "rating", "SubmitReview", "SubmitComment", "comment", "topLevel", "CozeKey", "Error", "cb", "returnReplyCallback", "coze", "api", "CommentUpdate", "API", "parsd", "AccountDetails", "createComment", "getCommentTextDigest", "CommentCreate", "formData", "FetchPost", "id", "commentElem", "edit", "parent", "ce", "createExtraAppend", "card", "SubmitReview", "pay", "setReviewPay", "commentThread", "createReview", "JumpToAnchor", "reviewDiv", "reviewObj", "PR", "commentDelete", "CommentDelete", "reviewDetails", "ToggleReviewForm", "records", "hide", "i", "uad", "UAD", "CreatePreInviteEmailForm", "CreateSubmitReviewForm", "SetRatingStar", "rating", "stars", "SetPageRatingStar", "getStarsFromRating", "starhtml", "starNum", "dataRating", "Err", "DownloadWallet", "keys", "extra", "filename", "downloadLink", "EmailWallet", "email", "formData", "EmailBackupCreate", "AccountDetails", "resp", "FetchPost", "API", "UpsertKey", "ck", "callback", "k", "UAD", "Error", "Err", "KeyUpsert", "Coze", "AddOnloadFirst", "LoginOnload", "UAD", "AccountDetails", "CozeKey", "CozeKeyExtra", "CozeKeyPublic", "Keys", "Accounts", "LoginKeySuccess", "preInviteModal", "errPreInviteAccountAlreadyCreated", "LS_SelectedKey", "LS_Keys", "LS_AccountDetails", "LS_Accounts", "LoginOnload", "InitFromStorage", "l", "preinviteAndBackupPesterModal", "Login", "cozeKey", "callback", "formData", "genLoginRequest", "KeyUpsert", "loginCallback", "FetchPost", "API", "e", "Error", "generatePreInviteAccount", "quags", "GlobalNewKey", "SetSelectedKey", "parsd", "elem", "SetLocalAccount", "Notification", "CreateSubmitReviewForm", "error", "Logout", "noRedirect", "IsLoggedIn", "pay", "coze", "IsUserBackedUp", "hasPreInviteModal", "response", "FetchHTML", "Page", "div", "ck", "accountModal", "modalContent", "InitKeys", "notBackedUp", "currentWallet", "k", "key", "atLeastOneChecked", "EmailWallet", "DownloadWallet", "UpsertKey", "UpsertGlobalKeys", "userIsBackedUp", "CreatePreInviteEmailForm", "emailForm", "event", "submitPreInviteEmail", "email", "displayName", "Site", "resp", "initSelected", "initAccountFromStorage", "ocl", "oclImage", "string", "s", "InitAccounts", "stringedAccts", "stringedKeys", "DeleteKey", "id", "DeleteLocalAccount", "uad", "UpsertGlobalAccounts", "UpdateKid", "tmb", "newKid", "account", "UpdateAccount", "SetLocalAccounts", "UpdateLocalAccountDisplayName", "IsCyphrmeDigest", "UpdateAccountDisplayNames", "accounts", "GetProfile", "fields", "p", "f", "cz", "UpsertGlobalKey", "alg", "VerifyAccess", "AccountDetailsCallback", "ClearLocalStorage", "CozeKeyNormal", "nck", "Dragster", "el", "b", "fn", "me", "event", "dropzone", "dragster", "gallery", "dragsterDropFunction", "dragsterProgressFunction", "DragsterOnload", "handleDrop", "eventName", "preventDefaults", "HandleFiles", "DragsterSetCallback", "callback", "DragsterSetProgressCallback", "dt", "files", "blobs", "index", "item", "blob", "reader", "i", "previewFile", "file", "IsImage", "DragsterLoaded", "ImagePage", "CurrentImageDig", "CurrentImageID", "CurrentImageRoot", "ImageLimit", "imageModal", "Ime", "ImageCzdObj", "FilePage", "FileCzdObj", "FileLimit", "ImageOnload", "imagePage", "DragsterSetCallback", "uploadImage", "DragsterOnload", "DisplayImages", "FileOnload", "filePage", "delBtn", "l", "DeleteFile", "DisplayFiles", "btns", "btn", "Page", "setModalArrows", "keyArrows", "event", "e", "appendTmbImageToGallery", "colCounter", "cols", "fileCounter", "i", "appendTmbFileToGallery", "UAD", "div", "file", "listDiv", "li", "API", "check", "input", "image", "imageDiv", "img", "src", "setImageModal", "czd", "left", "czdKeys", "ind", "GetFileExtension", "filename", "parts", "IsImage", "fileName", "DownloadTxt", "txt", "downloadLink", "UploadFile", "file", "UAD", "Error", "MaxFileSize", "ImagePage", "coze", "FileCreate", "formData", "parsd", "FetchPost", "API", "e", "uploadImage", "cp", "image", "ImageCzdObj", "appendTmbFileToGallery", "Notification", "appendTmbImageToGallery", "DeleteFile", "czd", "CurrentImageID", "FileDelete", "FileCzdObj", "Coze", "Lib", "ACFormOptions", "BLACKLIST_ACApplicationFormParameters", "BLACKLIST_ACPrintOptionFormParameters", "BLACKLIST_ACFormParameters", "ACNormal", "formKeys", "updateButton", "submitOptionsCallback", "originalAC", "InitSubmitOptions", "SubmitOptionsCallback", "SetOriginalAC", "originalac", "initedFormOptions", "InitACOptions", "formOptions", "skipUpdateBtn", "event", "UAD", "Error", "standard", "formData", "ACUpdate", "FetchPost", "API", "modelID", "IsCyphrmeDigest", "Notification", "item", "validJSON", "parsd", "RecalcOptionGUI", "recalculatePayFromACNormal", "newAC", "ProcessOpts", "ACGetOptionsExtra", "error", "AcsFromTree", "pjArgs", "acs", "id", "ac", "CozeKey", "QRCodeURL", "CheckModelParent", "ACGetOptions", "obj", "acOptions", "key", "ACGetBlacklistedPrintOptions", "printOpts", "optional", "ex", "extra", "v", "ACRegenPay", "oldPay", "opts", "typ", "Typs", "pay", "IsBASE37", "acNotGenerated", "resign", "currentSupportedOptions", "p", "param", "userJSON", "optInfoElem", "normal", "need", "opt", "GetStickerTitle", "stickerTitle", "ld", "Coze", "Lib", "Typs", "GenCoze", "pay", "cozeKey", "CozeKey", "GenCozeWithMeta", "coze", "meta", "GenPayStandard", "ACUpdate", "standard", "ACRegenPay", "ACGetOptionsExtra", "PrintJobCreate", "pj", "PrintJobUpdate", "BundleCreate", "bundle", "KeyUpsert", "upsertCozeKey", "signCozeKey", "ck", "KeyDelete", "tmb", "KeyRevoke", "msg", "KeyOtherRevoke", "InviteCreate", "uad", "displayName", "InviteDelete", "id", "EmailBackupCreate", "email", "EmailVerifyCreate", "ProfileUpdate", "profile", "IsCyphrmeDigest", "Error", "key", "ProfilePictureUpdate", "file", "ext", "GetFileExtension", "UAD", "commentCreate", "typ", "cp", "edit", "field", "CommentCreate", "CommentUpdate", "CommentDelete", "FileCreate", "root", "child_ac", "FileDelete", "czd", "btItem", "btPage", "btBatch", "btBox", "btPallet", "btRack", "btWarehouse", "BundleTypes", "BundleCVAsFromTree", "tree", "BCVA", "numerator", "denominator", "child", "thing", "BundleCreate", "DecrementBundle", "bundle", "Error", "BLSTopToString", "bls", "level", "BLSItemCount", "sum", "depth", "BLSToGUIString", "bls", "s", "CreateQR", "qrText", "qrdiv", "DefaultLabelOptions", "CreateLabelElement", "qrURL", "lblOpts", "printLabel", "innerDiv", "hrDiv", "setLabelBranding", "qr", "CreateQR", "CreateACLabel", "parentDiv", "templ", "template", "Labels", "css", "CreateCustomLabelCSS", "s", "isTitleSet", "customMainLogo", "CustomPrintCSS", "cpp", "CreateCustomPageCSS", "margBottom", "landscape", "authenticAndTitleFontSize", "qrFloat", "r90", "standardUSLetter", "standardRoll", "standardCyphrmeMargins", "cyphrmeBrandingLvl1", "noCheckboxOpts", "noDebugFields", "delBtns", "InitTableButtonsToggle", "opts", "item", "b", "btn", "SetCheckBoxModals", "setDeleteModal", "dme", "deleteModal", "SetModalPreview", "bsModalInstance", "divListID", "list", "items", "r", "name", "id", "DeleteTableRow", "parsd", "checkBoxOpts", "Notification", "NormalizeCheckBoxOptionsRecordMap", "record", "rowID", "rowTitle", "InitRowCheckbox", "f", "input"] }