UNPKG

@wider/utils_proto

Version:

A set of extensions to basic objects giving uniform behaviour in various technical environments

817 lines (680 loc) 88.3 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Source: proto_string/index.js | YDR</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/bootstrap.min.css"> <link type="text/css" rel="stylesheet" href="styles/prettify-jsdoc.css"> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/tui-doc.css"> </head> <body> <nav class="lnb" id="lnb"> <div class="logo" style="width: 50px"> <a href="http://y-d-r.co.uk" rel="noopener noreferrer" target="_blank"> <img src="http://y-d-r.co.uk/favicon.ico" width="100%" height="100%"> </a> </div> <div class="title"> <h1><a href="index.html" class="link">YDR</a></h1> <span class="version">v1.0.23</span> </div> <div class="search-container" id="search-container"> <input type="text" placeholder="Search"> <ul></ul> </div> <ol class="lnb-tab"> <li id="api-tab"> <a href="#"><h4>API</h4></a> </li> <li id="examples-tab"> <a href="#"><h4>Examples</h4></a> </li> </ol> <div class="lnb-examples hidden"><h3>Examples</h3><ul><li><a href="tutorial-Using Markdown with ydr_HTMLencode.html">Using Markdown with ydr_HTMLencode</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="Using Markdown with ydr_HTMLencode_sub"></div></li><li><a href="tutorial-Using Object Prototype extensions.html">Using Object Prototype extensions</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="Using Object Prototype extensions_sub"></div></li></ul></div><div class="lnb-api hidden"><h3>Modules</h3><ul><li><a href="module-@wider_utils_proto.html">@wider/utils_proto</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto_sub"><div class="member-type">Methods</div><ul class="inner"><li><a href="module-@wider_utils_proto.html#~init">init</a></li></ul></div></li><li><a href="module-@wider_utils_proto_proto_array.html">@wider/utils_proto/proto_array</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_array_sub"><div class="member-type">Methods</div><ul class="inner"><li><a href="module-@wider_utils_proto_proto_array.html#~keySort()">keySort()</a></li><li><a href="module-@wider_utils_proto_proto_array.html#~numericSortAsc()">numericSortAsc()</a></li><li><a href="module-@wider_utils_proto_proto_array.html#~permute()">permute()</a></li><li><a href="module-@wider_utils_proto_proto_array.html#~plainSortAsc()">plainSortAsc()</a></li><li><a href="module-@wider_utils_proto_proto_array.html#~plainSortDesc()">plainSortDesc()</a></li><li><a href="module-@wider_utils_proto_proto_array.html#~pushUnique()">pushUnique()</a></li></ul></div></li><li><a href="module-@wider_utils_proto_proto_array_test.html">@wider/utils_proto/proto_array/test</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_array/test_sub"></div></li><li><a href="module-@wider_utils_proto_proto_date.html">@wider/utils_proto/proto_date</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_date_sub"><div class="member-type">Methods</div><ul class="inner"><li><a href="module-@wider_utils_proto_proto_date.html#~toISOString">toISOString</a></li></ul></div></li><li><a href="module-@wider_utils_proto_proto_date_test.html">@wider/utils_proto/proto_date/test</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_date/test_sub"></div></li><li><a href="module-@wider_utils_proto_proto_function.html">@wider/utils_proto/proto_function</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_function_sub"><div class="member-type">Methods</div><ul class="inner"><li><a href="module-@wider_utils_proto_proto_function.html#~describe()">describe()</a></li></ul></div></li><li><a href="module-@wider_utils_proto_proto_function_test.html">@wider/utils_proto/proto_function/test</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_function/test_sub"></div></li><li><a href="module-@wider_utils_proto_proto_number.html">@wider/utils_proto/proto_number</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_number_sub"><div class="member-type">Methods</div><ul class="inner"><li><a href="module-@wider_utils_proto_proto_number.html#~bitsUpTo()">bitsUpTo()</a></li><li><a href="module-@wider_utils_proto_proto_number.html#~two()">two()</a></li></ul></div></li><li><a href="module-@wider_utils_proto_proto_number_test.html">@wider/utils_proto/proto_number/test</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_number/test_sub"></div></li><li><a href="module-@wider_utils_proto_proto_object.html">@wider/utils_proto/proto_object</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_object_sub"><div class="member-type">Methods</div><ul class="inner"><li><a href="module-@wider_utils_proto_proto_object.html#~assign()">assign()</a></li><li><a href="module-@wider_utils_proto_proto_object.html#~deepMerge()">deepMerge()</a></li></ul></div></li><li><a href="module-@wider_utils_proto_proto_object_test.html">@wider/utils_proto/proto_object/test</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_object/test_sub"></div></li><li><a href="module-@wider_utils_proto_proto_string.html">@wider/utils_proto/proto_string</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_string_sub"><div class="member-type">Methods</div><ul class="inner"><li><a href="module-@wider_utils_proto_proto_string.html#~asHTML()">asHTML()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~attrEncode()">attrEncode()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~camelIse()">camelIse()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~checkSum()">checkSum()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~checkSumValidate()">checkSumValidate()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~contextParse()">contextParse()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~deCamelIse()">deCamelIse()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~ellipses()">ellipses()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~escape()">escape()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~extendedCharCodeFixer()">extendedCharCodeFixer()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~formStrToXMLattr()">formStrToXMLattr()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~fuzzyValue()">fuzzyValue()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~HTMLencode()">HTMLencode()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~HTMLencodeInit">HTMLencodeInit</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~isHTML()">isHTML()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~isHTMLerror()">isHTMLerror()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~proper()">proper()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~regExpEncode()">regExpEncode()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~replaceSafe()">replaceSafe()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~searchEncode()">searchEncode()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~selectorEncode()">selectorEncode()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~simpleHTMLtoPlain()">simpleHTMLtoPlain()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~standardMarkups()">standardMarkups()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~stringEncode()">stringEncode()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~textSignature()">textSignature ()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~trim()">trim()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~unescape()">unescape()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~unHTMLencode()">unHTMLencode()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~wrapAsError()">wrapAsError()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~wrapAsOK()">wrapAsOK()</a></li><li><a href="module-@wider_utils_proto_proto_string.html#~XMLverify()">XMLverify()</a></li></ul></div></li><li><a href="module-@wider_utils_proto_proto_string_contextParse.html">@wider/utils_proto/proto_string/contextParse</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_string/contextParse_sub"><div class="member-type">Typedef</div><ul class="inner"><li><a href="module-@wider_utils_proto_proto_string_contextParse.html#~rowId">rowId</a></li><li><a href="module-@wider_utils_proto_proto_string_contextParse.html#~wider_context">wider_context</a></li></ul></div></li><li><a href="module-@wider_utils_proto_proto_string_test.html">@wider/utils_proto/proto_string/test</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_string/test_sub"></div></li></ul></div><div class="lnb-api hidden"><h3>Classes</h3><ul><li><a href="module-@wider_utils_proto_proto_string_contextParse-ContextParse.html">ContextParse</a><button type="button" class="hidden toggle-subnav btn btn-link"> <span class="glyphicon glyphicon-plus"></span></button><div class="hidden" id="module:@wider/utils_proto/proto_string/contextParse~ContextParse_sub"><div class="member-type">Methods</div><ul class="inner"><li><a href="module-@wider_utils_proto_proto_string_contextParse-ContextParse.html#toString">toString</a></li></ul></div></li></ul></div> </nav> <div id="resizer"></div> <div class="main" id="main"> <section> <article> <pre class="prettyprint source linenums"><code>'use strict'; import ContextParse from "./contextParse.js"; import $wider from "@wider/registry"; const $moduleName = "@wider/utils_proto/proto_string"; let htmlEntities; let HTMLsafeRE; let markdown; // the instance for default markups using the settings given in .init() const CONFIG = $wider.CONFIG; /** * A series of methods for the javaScript String Object, all methods have names starting `wider_` * * * This provides a collection of methods that extend the javascript String object to allow simpler and easier to read code. Elsewhere many javascript string processing function are declared as stand alone procedures and not as properties if the String object and may have different code implementations in different environments/version or not available in all environments. * * For example in older implementations of javascript `escape()` would give different results in different environments / versions and is not even available in later versions * * When used in multiple combinations as prefix function calls, the standard javascript code is needlessly inelegant and harder to maintain - either requiring numerous separate statements or nested method calls which are hard to read amd have a risk of misplaced parentheses or even parameters. * * With these methods in this module you can blend native postfix method calls as well as these custom methods with code that is easy to read. * * The exported object of this module is an object to be assigned to your `String` class or your extension thereof. The methods assigned to the class are the methods of this module which have parentheses appended to their method name. To avoid current or future risk of name conflict, all names are prefixed with `.wider_` when used - for example * * ```javascript * "myString".wider_camelIse() *``` * * @module @wider/utils_proto/proto_string * * @copyright Copyright (C) 1985..2021 Martin Baker. http://y-d-r.co.uk * @author Martin W Baker * @license ISC Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ /** * @example * import proto_string from "ydr/utils_proto/proto_string") * (String); * getAstringFromSomeWhere * .trim() * .deCamelIse() * .replace(/First/, "Last") // native javascript * .proper("throughout") */ // this jsDoc is here but is for the version as used in assignString /** * Convert a string so that it is safe for use as text content in HTML. If the string is already HTML it will still be encoded - rendering the HTML markups visible to a user viewing the result. * * See also `myString.wider_attrEncode()` for strings that are safe to place inside an xml or HTML attribute * See also `myString.wider_asHTML()` which only converts if the string is not already HTML. * @function HTMLencode() * @param {enumList} [condition] values `'newlines'HTML'markdown` * * If this contains `HTML` then if the string already appears to be HTML syntax then it is not amended * * if this contains `newlines` then new line markers are replace with their HTML character code equivalent * * if this contains 'markdown' then ***markdown-it*** will be used if the string appears to be markdown. Your must call `"".wider_HTMLencode("markdownInit") ONCE in lifetime before calling with this option * * if this is ONLY 'markdownInit' then the markdown tool is loaded dynamically on its first use to prevent bloat if it is not used. So if you intend to use this feature then call `"".wider_HTMLencode("markdown", [settings])` to initialise. The result will be a ***Promise*** that triggers when ready. In general, unless you are initialising just in time, you cam ignore the Promise if you execute this call immediately you have imported this module. See example in the *test.js* source code * @param {Object} [settings] if present then defines the custom settings for this call only * @returns {string} the marked up string */ function HTMLencode( condition, settings ) { /* per @wider/utils_bundle.basics but also combines asHTML - discretionary HTML encoding if the text is not already html alike this combo of HTMLencode and asHTML allows the caller to put the condition in an IF statement eg see in ydrFunctions:formattedXml &lt;span style="color:blue">"&lt;xsl:value-of select="ydrFunctions:HTMLencode(., ydrFunctions:ydrIf($mode='displayHTMLcontent', '', 'HTML'))" />"&lt;/span> which makes an attribute that has HTML in it normally show the raw HTML but if $mode='displayHTMLcontent' then provides a formatted rendering so HTMLencode("sfgdfsgsdf", "HTML") here is the same as "LKJHKLHLK".wider_HTMLencode() in node / web javascript for future code always call asHTML if that is the functionality required in all cases newlines causes newlines to be made hard and also suppresses markdown type encoding */ var result = this; try { if ( /newlines/.test( condition ) ) result = result.replace( /\n/g, "&amp;#xa;&amp;#xa;" ); /* dilemma in the commented replace in second version if not present then doesn't double encode ampersaND */ if ( !/HTML/.test( condition ) &amp;&amp; result.wider_isHTML() ) {} else { if ( /markdown/.test( condition ) ) { if ( result.wider_isHTML() ) { // leave as it is } else if ( !markdown ) throw new Error( assignString.$moduleName + "/wider_HTMLencode(\"markdown\") called without being initialised - call the .init() method first - continuing with default settings" ); else { const myMarkdown = settings ? new $wider.markdownIt.default( settings.markdown ) : markdown; if ( result[ 0 ] === "#" ) result = myMarkdown.render( result ); else result = myMarkdown.renderInline( result ); } } else /* DEPRECATED - a markdown string must start and end with a newline (not a mandatory standard for maRkdown but permitted - since not all strings have to be markdown we check for this or condition must be markdown first we collapse bullets (only first level supported here we also do a quicker check so we avoid the cpu if there is nothing to do */ if ( /markdownOLD|md/.test( condition ) &amp;&amp; /[\n\*#]/.test( result ) ) { /* standardised for processing TODO detect non HTML &lt;> and encode them - leaving real &lt;> unaffected this is crude at the moment and only looks for &lt; not followed by pur alphas and > not preceded by pur alphas - this is is risky example "sdfgsd&amp;cvbn fgbxc&amp;sdfgsdf;cvghgd&lt; &lt;\sdfg&lt;sdfg \"\"jhjkldsfhg/> sfghsfhf&lt;span class=\"bold\" id='sdfg'>ghfs&lt;/span>" becomes ' sdfgsd&amp;amp;cvbn fgbxc&amp;sdfgsdf;cvghgd&amp;lt; &amp;lt;sdfg&lt;sdfg ""jhjkldsfhg/&amp;gt; sfghsfhf&lt;span class="bold" id='sdfg'&amp;gt;ghfs&lt;/span> ' */ result = ( "\n" + result + "\n" ) .replace( /&amp;(?![\d\w#]+;)/g, "&amp;amp;" ) .replace( /&lt;(?!\/?[^&lt;>]+\/?>)/g, "&amp;lt;" ) .replace( /(?&lt;!&lt;\/\w[\w_\d:]*)>/g, "&amp;gt;" ); /* this first fixes make legacy ydr markdown and its extensions work - example includes the wrapping \n that would be applied by the first fix above dealing with &lt;h1> etc noting that you can place class names before a first colon - ignoring a colon at the end of a line "\n*head=1 this is my headings\nsdfgsdf this is my content\n\n*head=3 shadow italic: that is my heading\n\nsdfgsd that is too\n*head=4 and these are not css classes:\nso there\n" becomes ' &lt;h1> this is my headings&lt;/h1> sdfgsdf this is my content &lt;h3 class="shadow italic"> that is my heading&lt;/h3> sdfgsd that is too &lt;h4> and these are not css classes:&lt;/h4> so there ' */ result.replace( /\*head=(1|2|3|4|5|6)([^\n]+)(?=\n)/g, function ( match, depth, body ) { let firstColon = body.indexOf( ":" ); if ( firstColon === ( body.length - 1 ) ) firstColon = -1; const classes = body.substr( 0, firstColon ).trim(); return "&lt;h" + depth + ( classes ? " class=\"" + classes + "\"" : "" ) + ">" + body.substr( firstColon + 1 ) + "&lt;/h" + depth + ">"; } ) /* likewise deal with *p *p=classes etc: *text=classes etc -- gives &lt;p class="classes">... and &lt;span class="classes">... note we have to do spans first in case they are wrapped by a p " sdfggsf *p dfhdfshsfd text=bold italic: ssdfgsdgs zxcvzxcvzxc text=blink: look blinky to me *p wont be seen as not preceded by \n\n sdfgsdg *p hidden: and this p will be encoded so it cannot be seen on the web page &lt;&lt;no return>> "into javascript "\n sdfggsf\n\n*p dfhdfshsfd\n*text=bold italic: ssdfgsdgs\nzxcvzxcvzxc\n*text=blink: look blinky to me\n*p wont be seen as not preceded by \\ n \\ n\nsdfgsdg\n\n*p hidden: and this p will be encoded so it cannot be seen on the web page" note that the p has to end with two \n\n and the span one doesn't with span and p leave one \n afterwards becomes ' sdfggsf dfhdfshsfd&lt;span class="=bold italic"> ssdfgsdgs&lt;/span> zxcvzxcvzxc&lt;span class="=blink"> look blinky to me&lt;/span> *p wont be seen as not preceded by \ n \ n sdfgsdg &lt;p class="hidden"> and this p will be encoded so it cannot be seen on the web page ' */ .replace( /\n\*text([^\n]+)(?=\n)/g, function ( match, body ) { let firstColon = body.indexOf( ":" ); if ( firstColon === ( body.length - 1 ) ) firstColon = -1; const classes = body.substr( 0, firstColon ).trim(); return "&lt;span" + ( classes ? " class=\"" + classes + "\"" : "" ) + ">" + body.substr( firstColon + 1 ) + "&lt;/span>"; } ) .replace( /\n\*p([\s\S]*?)(?=(\n\n)|$)/g, function ( match, body ) { let firstColon = body.indexOf( ":" ); if ( firstColon === ( body.length - 1 ) ) firstColon = -1; const classes = body.substr( 0, firstColon ).trim(); return "\n&lt;p" + ( classes ? " class=\"" + classes + "\"" : "" ) + ">" + body.substr( firstColon + 1 ) + "\n"; } ) /* now we have a special ydr markdown for consents *consent or *consent:consent instructions *consent:sdfhsdfhd_dfghdfhgffd *consent *consent=sdfgsdfgsdgf as javascript "\n*consent:sdfhsdfhd_dfghdfhgffd\n*consent\n*consent=sdfgsdfgsdgf" becomes */ .replace( /\n\*consent[:=]?([^\n]*)(?=(\n|$))/g, function ( match, body ) { // use attrEncode so no error is thrown here if there is an error (eg by user typing) - let the error happen later let instructions = body.substr( body.indexOf( ":" ) + 1 ).trim().wider_attrEncode(); //either rowidlist or application name return "&lt;div>&lt;wider_consent " + ( /_/.test( instructions ) ? "consents=\"" + instructions.wider_attrEncode() + "\"" : "params=\"" + instructions.wider_attrEncode() + "\"" ) + "/>&lt;/div>\n"; } ) /* this makes the following subset of standard markdown work */ .replace( /\n[\t ]*\*([^\n]*)(?=\n)/g, "&lt;li>$1&lt;/li>" ) .replace( /\n[\t ]*#####([^\n]+)(?=\n)/g, "&lt;h6>$1&lt;/h6>" ) .replace( /\n[\t ]*####([^\n]+)(?=\n)/g, "&lt;h5>$1&lt;/h5>" ) .replace( /\n[\t ]*###([^\n]+)(?=\n)/g, "&lt;h4>$1&lt;/h4>" ) .replace( /\n[\t ]*##([^\n]+)(?=\n)/g, "&lt;h3>$1&lt;/h3>" ) .replace( /\n[\t ]*#([^\n]+)(?=\n)/g, "&lt;h2>$1&lt;/h2>" ) .replace( /\n[\t ]*\n/g, "" ) .replace( /\n([^\n]*)(?=\n)/g, "$1" ) .replace( /&lt;br\/>&lt;br\/>([\s\S]*)&lt;br\/>&lt;br\/>/g, "$1" ) .replace( /(&lt;li>.*?&lt;\/li>)&lt;\/p>/g, "&lt;ul>$1&lt;/ul>" ) .trim() .replace( /&lt;\/p>/g, "" ) .replace( /\n/g, "&lt;br/>" ) .replace( /(?&lt;!\*)\*\*\*([^*]+)\*\*\*/g, "&lt;span class=\"bold italic\">$1&lt;/span>" ) .replace( /(?&lt;!\*)\*\*([^*]+)\*\*/g, "&lt;span class=\"bold\">$1&lt;/span>" ) .replace( /(?&lt;!\*)\*([^*]+)\*/g, "&lt;span class=\"italic\">$1&lt;/span>" ) ; } else result = result .replace( /&amp;/g, "&amp;amp;" ) .replace( /&lt;/g, "&amp;lt;" ) .replace( />/g, "&amp;gt;" ) .replace( /\n|\r/g, '&lt;br/>' ) .replace( "/--/g", "-&amp;#45;" ); } } catch ( e ) { result = "&lt;span class='error'>" + assignString.$moduleName + ":" + e.message + ": " + ( typeof text ) + ": " + result + "&lt;/span>"; console.log( result ); } return result; } /** * Initiates dynamic loading of the markdown utility within HTMLencode and sets its defaults - onluy does so if markdownIt is present in global.$wider * @param {Object} [settings] the markdown-it settings to be used * @returns {Promise} fires when ready - in most usages you do not need to wait on this Promise - see worked example * @throws {Error} if solution has not loaded markdown-it */ function HTMLencodeInit( _settings = { "breaks": true, "linkify": true } ) { const settings = Object.assign( {}, _settings, { "html": true, "xhtmlOut": true, } ); if ($wider.markdownIt) markdown = new $wider.markdownIt.default(settings); } /* for wider_standardMarkups() */ const m_simpleMarkupRe = /\xa0\w+\xa0|&amp;nbsp;\w+&amp;nbsp;|&amp;#160;\w+&amp;#160;/; // eg for %year% and %siteInitials% // note keep in synch :: resources help "".wider_standardMarkups //TODO /* WARNING for unStandardMarkups to work 100% use the span/@data-ydr-markup AND DO NOT include any further embedded &lt;span/> note that file references must not be wrapped in SPAN as that could mess with rendering of embedded code urls etc the ...AE (attributeEncoded) versions are for when eg some one does &lt;div ... data-ydr-title="my report for %yearAE%">This %year% we had a good turnout .... ...AE versions dont UNMARKUP so should be avoided for database use. mostly values are placed in a span so they can be unMarkedUp but some which related to code (eg urls cannot be marked up) but in n those cases they can always be unMarkedUp from their original string */ /* important the following are not expanded until point of use as the expansion is different eg at panel in webpage versus in email %administrator% -- delivers an anchor to allow anyone to contact the administrator */ const m_markupMap = { "%newWindow%": "&lt;span data-ydr-markup=\"newWindow\" contenteditable=\"false\">&amp;#160; &lt;img class=\"inline notPrintable\" src=\"\" + config.ydrDir + \"/images-f/arrow_branch.png\" alt=\"result opens a new window, the current window remains unchanged\" />&lt;/span>", "%download%": "&lt;span data-ydr-markup=\"download\" contenteditable=\"false\">&amp;#160; &lt;img class=\"ydr-menu-text notPrintable\" src=\"\" + config.ydrDir +\"/images-y/download.png\" alt=\"download\" title=\"download a document\" />&lt;/span>", "%siteName%": CONFIG.siteName /* eg York development and research*/ /*, "%siteName%" : "&lt;span data-ydr-markup=\"siteName\">" + config.siteName + "&lt;/span>" */ /* eg York development and research*/ , "%domain%": CONFIG.domain /*eg y-d-r.co.uk */ , "%emailDomain%": CONFIG.emailDomain /*eg y-d-r.co.uk */ , "%aliasesDivert%": CONFIG.aliasesDivert /* deprecated */ , "%solutionVersionApp%": CONFIG.solutionVersionApp /* for placing in eg js / css urls in HTML to ensure cache flushed at each new server load */ /*DEPRECATED*/ , "%siteInitials%": "\xa0" + CONFIG.siteInitials + "\xa0" /* eg YDR */ , "%YDR%": "\xa0YDR\xa0" /* excluded from unstandardMarkups */ , "%XPATH%": "&lt;a href=\"https://www.w3Schools.com/xml/xpath_intro.asp\" target=\"_blank\">international XPATH standard&lt;/a>" }; function markupSession() { const session = $wider.session; if ( !session.markups ) session.markups = { markupMap: $wider.utils.deepCopy( m_markupMap ), unMarkupMap: $wider.utils.deepCopy( m_unMarkupMap ) }; session.markups.markupMap[ "%rights%" ] = session.rights; /* eg "'guest'user'manager'" */ session.markups.markupMap[ "%userState%" ] = ( $wider.core_data || {} ).userState || "unknown"; /*eg normal */ // as this can change it cannot be in the static block session.markups.markupMap[ "%year%" ] = "\xa0" + new Date().getFullYear() + "\xa0" /*eg 2020 */ ; return session; } // see also $wider.Fso.unStandardMarkups() //TODO /* for wider_unStandardMarkups() */ const m_unMarkupMap = {}; for ( const v in CONFIG.markup ) { if ( m_simpleMarkupRe.test( CONFIG.markup[ v ] ) ) m_unMarkupMap[ CONFIG.markup[ v ] ] = v; } const stringPrototypes = { /** * Turns any string into a camelised identifier - action is mostly reversed by ```.deCamelIse()```. * * In order to be reversible the camelisation process is slightly different to the norm when dealing with the transition between an acronym and a following name. So ***an XML string*** becomes ***anXMLstring*** and hence * * ```javascript * const demo = "an XML string"; * demo.wider_camelIse().deCamelIse() == demo; // true * ``` * * * BUT if the string starts with a hard space the first hard space is removed and the string left otherwise unchanged. * * Note the camelisation of this function's name * * `.wider_camelIse()` and `.wider_deCamelIse()` are often used with enumerated lists enabling a data value to have the form of an objectname convenient for data use and to generate a neat alternative for the user to read and write * @function camelIse() * @returns {string} * @example * "hold my hands up".camelIse() // holdMyHandsUp * "HTML encode".camelIse() // HTMLencode */ wider_camelIse: function () { if ( /$\xA0/.test( this ) ) return this.substr( 1 ); let parts = this.trim().replace( /[^A-Za-z0-9 ]/g, "" ).split( /\s+/ ); for ( let i = 0; i &lt; parts.length; i++ ) //start at 1 !!! { if ( parts[ i ].search( /^[A-Z0-9].*[A-Z]$/ ) >= 0 ) parts[ i ] = parts[ i ].toUpperCase(); else if ( i == 0 ) parts[ i ] = parts[ i ].toLowerCase(); else parts[ i ] = parts[ i ].charAt( 0 ).toUpperCase() + parts[ i ].substr( 1 ).toLowerCase(); if ( i > 0 &amp;&amp; ( parts[ i - 1 ].charAt( parts[ i - 1 ].length - 1 ) == parts[ i - 1 ].charAt( parts[ i - 1 ].length - 1 ).toUpperCase() ) ) //parts[i] = parts[i].substr(1).toLowerCase() parts[ i ] = parts[ i ].charAt( 0 ).toLowerCase() + parts[ i ].substr( 1 ).toLowerCase(); } return parts.join( "" ); }, /** * Turns any camelised string into a space separated string - action is mostly reversed by .camelIse * * BUT if the string starts with a hard space the first hard space is removed and the string left otherwise unchanged. * * Note the camelisation of the function name * `.camelIse()>` and `.deCamelIse)` are often used with enumerated lists enabling a data value to have the form of an objectname and to generate a neat alternative for the user and to work back from what the user enters to the objectname * @function deCamelIse() * @returns {string} * @example * "aasdf".deCamelIse() // aasdj * * "holdMyHandsUp".deCamelIse() // "hold my hands up" * "HTMLencode".deCamelIse().camelIse() // "HTMLencode" * "wasThatIBMsoftware".deCamelIse() // "was that IBM software" * "HTMLEncode".deCamelIse() // "HTMLEncode" --- note shown to avoid inelegant forms * "HTMLEncode".deCamelIse().camelIse() // "HTMLencode" */ wider_deCamelIse: function ( /*enum |isolateNumbers|*/ mode, /* string */ spaceChar ) { /* typically given an id or object name in camel format, turns it into a user string spaced out at the camelising warning remember that toUpperCase comparison is NOT the same not the toLowerCase comparison if the string contains any spaces then this prefixes the string with a hard space and otherwise leaves it alone any string provided to deCamelIse will be reverted to its original self by camelIse */ let result; if ( !/[a-zA-Z]/.test( this ) ) result = this; // this detects if this is a de-camelising candidate so eg starts with a number or symbol is not else if ( / /.test( this ) ) // names that already have a space in them are left alone but given hard spaces result = this.replace( / /g, spaceChar || "�" /* a hard space*/ ); else { let chars = this.split( "" ); for ( let i = chars.length - 1; i > 0; i-- ) if ( mode === "isolateNumbers" &amp;&amp; /[0-9]/.test( chars[ i ] ) ) { if ( !/[0-9]/.test( chars[ i - 1 ] ) ) chars[ i ] = " " + chars[ i ]; } else if ( chars[ i ] != chars[ i ].toLowerCase() &amp;&amp; chars[ i + 1 ] ) { if ( chars[ i - 1 ] != chars[ i - 1 ].toUpperCase() ) chars[ i ] = " " + ( ( chars[ i + 1 ] == chars[ i + 1 ].toUpperCase() ) ? chars[ i ] : chars[ i ].toLowerCase() ); else if ( chars[ i + 1 ] &amp;&amp; ( chars[ i + 1 ] != chars[ i + 1 ].toUpperCase() ) ) chars[ i + 1 ] = " " + chars[ i + 1 ]; } if ( chars[ 1 ] != chars[ 1 ].toUpperCase() ) chars[ 0 ] = chars[ 0 ].toLowerCase(); result = chars.join( "" ); } return result + ""; }, /** * Generates a mod 97 checksum and appends it to the string. The string may contain formatting; any non digit character is excluded from the computation * Further checksum formats will be added according to pressures to include them * @function checkSum() * @param {enum} [mode=numeric97] the type of checksum required * &lt;ul>&lt;li>numeric97 = mod 97 all non digits are ignored - multiply the numeric value of each digit by its character position in [3,5,7,11,13,17,19,23,29,31] rotating so * 9876 * becomes * &lt;pre>` (9*3 + 8*5 + 7*7 + 6*11 ) mod 97 = 85`&lt;/pre> * @Example * "aasdf".wider_checksum() // "aasdf00" * "123456789123456789123456789123456789".wider_checkSum() // "12345678912345678912345678912345678904" * "GB 1245/6789-".wider_checkSum() // "GB 1245/6789-78" * @returns {string} the original value augmented by its checksum */ // &lt;li>IBAN - {@link https://en.wikipedia.org/wiki/International_Bank_Account_Number}&lt;/li> wider_checkSum: function ( mode ) { const multipliers97 = [ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 ]; let total = 0; let numbers; switch ( mode ) { case null: case "numeric97": numbers = this.replace( /[^\d]/, "" ); for ( let i = 0; i &lt; numbers.length; i++ ) if ( /\d/.test( numbers[ i ] ) ) total += numbers[ i ] * multipliers97[ i % 10 ]; return this + ( ( ( total % 97 ) &lt; 10 ) ? "0" : "" ) + ( total % 97 ); case "alphaNumeric97": numbers = this.replace( /[^\d[A=Za-z]]/, "" ).toUpperCase(); for ( let i = 0; i &lt; numbers.length; i++ ) if ( /\d/.test( numbers[ i ] ) ) total += numbers[ i ] * multipliers97[ i % 10 ]; else if ( /[A-Z]/.test( numbers[ i ] ) ) total += ( numbers.charCodeAt( i ) - 55 ) * multipliers97[ i % 10 ]; return this + ( ( ( total % 97 ) &lt; 10 ) ? "0" : "" ) + ( total % 97 ); case "IBAN": default: return this + "unrecognised checkSum mode " + mode; } }, /** * Generates a mod 97 checksum and appends it to the string. The string may contain formatting; any non digit character is excluded from the computation * @function checkSumValidate() * @param {enum} mode - the mode that the checksum would have been created in by `.checkSum()` * @returns {boolean} true if the checksum is valid as the last two characters of the string * @example * "aAsdf00".checkSumValidate() * // true * * "123456789123456789123456789123456789".checkSum() * // "12345678912345678912345678912345678904" * * "GB 1245/6789-".checkSum() * // "GB 1245/6789-78" */ wider_checkSumValidate: function ( mode ) // per data/ydrFunctions { return ( this == this.slice( 0, -2 ).checkSum( mode ) ); }, /** * Takes a context string and converts it into an object.This object may later be extended by the `$wider.data` to facilitate data updates or deletes or to create new rows * @function contextParse() * @example * "FINANCE:transactrowsRowId_4012:name:webfile".contextParse() * // ... * ContextParse { * urn: "FINANCE", * id: "transactrowsRowId_4012", * target: "transactrowsRowId_4012", * attribute: "name", * xpath: "webfile", * buttons: undefined, * selector: undefined, * action: undefined, * context: "FINANCE:transactrowsRowId_4012:name:webfile" * } */ wider_contextParse: function () { return new ContextParse( this ); }, /** * When one creates an xml object and its output contains extended character codes these can get mapped onto character code with values in excess of 127. In older systems with code pages you can get character corruptions when using database, javascript and client side HTML rendering. This is particularly prevalent when people paste in content from older applications such as older versions of MS Word. * This converts all unicode and extended character set copes above \x80 so that in effect you get UTF-7 rendering - which is carried safely in UTF-8 standard for javascript and various browser code pages * @function extendedCharCodeFixer() * @returns {string} HTML string compatible with the original but with extended characters * @throws {Error} if not part of a @wider/utils_bundle solution - so just dont use it */ wider_extendedCharCodeFixer: function ( /*optional enum |"noSessionData"| or the enums from standardPage().settings[responseRetain] |none|default|static|short|serverInstance|*/ mode ) { let result = this.replace( /[\u0080-\uffff]/g, function ( s ) { return "&amp;#" + s.charCodeAt( 0 ) + ";"; } ); /* let chars; let ch; let result = this; //if (this) //{ // sadly this code wont work because the Microsoft crap variant of javascript split fails to return // empty entries - something all other splits do - even in internet explorer / * let intermediate = result.split(/[^\x01-\x7f]/) let result = intermediate[0]; let position=-1; for (let i=1; i&lt;intermediate.length; i++) { position += intermediate[i-1].length+1; result += "&amp;#" + result.charCodeAt(position) +";" + intermediate[i] }* / ////////////////////// / * there are no characters under #128 that need mapping and all above - this avoids code page errors jumping from _ utf-8 iso-8859-1 ANSI etc - matters going from database to server code to html to client script to rendered html and _ likewise all the way back again as either queryString or data attached to XHR * / for (let i=0; i&lt;result.length; i++) { if (result.charCodeAt(i)>127) { if (chars == null) chars=result.split(""); chars[i]="&amp;#" + result.charCodeAt(i) +";" } }; //}; if (chars) { // session.debug.assert (result == chars.join("") || CONFIG.environment != "development", "bad fixer") result = chars.join(""); } */ const { session } = $wider; if ( ( !/text|csv|noSessionData/.test( mode ) ) &amp;&amp; ($wider.whereAmI.genre==="server") &amp;&amp; !/ data-ydr-response-retain="serverInstance"/.test( result ) ) result = result.replace( /(\/?>)/, $wider.serverScripts.sessionAttributes( session, mode ).replace( /\$/g, "$$$$" ) + "$1" ); return result; }, /** * Take a standard http request queryString (eg as in Request.queryString) or in formData and returns it as a string in the form of xml property name value pairs ready for insert in an xml element * eg `field1="lkjj;l" field2="lklkklh"` etc&lt;.p> unless mode is withNulls, empty or missing attributes are not included in the xml if the string has real unencoded newlines WITHIN an attribute value then these are JS escaped as is this is a formatting error * * @function formStrToXMLattr() * @param {enum} [mode] - values |withNulls| if present then attributes given in the source that have no value are supplied as empty strings otherwise they are suppressed * @returns {string} XML attribute list * @example * > "field0&amp;field1=here%20is%20my%20contentm%5bin%20here%5d&amp;field2=here%20is%20my%20content%20%3cin%20here%3e".formStrToXMLattr() * // "field1="here is my contentm[in here]" field2="here is my content &amp;amp;lt;in here&amp;amp;gt;" " * * > "field0&amp;field1=here%20is%20my%20contentm%5bin%20here%5d&amp;field2=here%20is%20my%20content%20%3cin%20here%3e".formStrToXMLattr("withNulls") * // "field0="" field1="here is my contentm[in here]" field2="here is my content &amp;amp;lt;in here&amp;amp;gt;" " */ wider_formStrToXMLattr: function ( mode ) { if ( this.length === 0 ) return ""; // people might have introduced line folding - dont want that so remove newlines and related white space - the &amp; indicates the join between one parameter and the next const arr = ( this + "" ).replace( /\s*\n\s*/, "" ).split( /&amp;+/ ); const result = []; for ( let i = 0; i &lt; arr.length; i++ ) { let arrI = arr[ i ].trim().split( /=/ ); if ( arrI[ 1 ] || mode === "withNulls" ) { result.push( arrI[ 0 ] + "=\"" + ( arrI[ 1 ] || "" ) .wider_unescape() .replace( /\+/g, " " ) .wider_attrEncode().wider_attrEncode() + "\" " ); } } return result.join( " " ); }, /** * Generates an encoding of a word or phrase that can be used to search a list - useful for use entry on long pick lists allowing typos and spelling errors to handled * * At the client, we suggest you use this method repeatedly with increasing rules until you get a short enough option list from your dataset * * @function fuzzyValue() * @param {enumList} [mode=basic] - zero or more of the values |basic|harsh|noVowels|alpha| * * ***basic*** - in which all spaces are removed, chars converted to lowercase, and utf-8 extended characters either removed or converted to lowercase a-z equivalents other characters are generally preserved * * ***harsh*** - removes all duplicate adjacent letters * * ***noVowels*** - aeiouy like letters removed (but accented ones kept) * * ***alpha*** - // only a-z A-Z \\xff * * @returns {string} } sting reduced for fuzzy comparison * @example * > "I have a sore Œsophagus".fuzzyValue() * "ihaveasoreœsopegus" * > "I have a sore Œsophagus".fuzzyValue("basic") * "ihaveasoreœsopegus" * > "I have a sore Œsophagus".fuzzyValue("harsh") * "I have a sore sophegus" * > "I have a sore Œsophagus".fuzzyValue("harsh noVowels") * "I hv sr sphgs" * > "I have a sore Œsophagus".fuzzyValue("basic harsh noVowels") * "hvsrœspgs" * > "I have 1 sore Œsophagus".fuzzyValue("alpha") * "havesoresophegus" */ wider_fuzzyValue: function ( mode ) { // these are the characters from code position 161 to 255 in utf-8 converted to a-z and 0 1 2 3 as relevant with � @ and � converted to a nd r const map = "��������ca��r���23�����10�����aaaaaaeceeeeiiiidnooooo�ouuuuy�saaaaaaeceeeeiiiionooooo��uuuuy�y"; const basic = /basic/.test( mode ) || !mode; const harsh = /harsh/.test( mode ) || basic; const noVowels = /noVowels/.test( mode ); const alpha = /alpha/.test( mode ); let ch; let result = ( basic ? this.replace( /\s+/g, "" ).toLowerCase() : this ).split( "" ); for ( let index = 0; index &lt; result.length; index++ ) { ch = this.charCodeAt( index ); if ( ch &lt; 32 || ( ch > 128 &amp;&amp; ch &lt; 162 ) || ch > 255 ) result[ index ] = ""; else if ( ch > 160 &amp;&amp; basic ) result[ index ] = map.charAt( ch - 162 ); else if ( harsh &amp;&amp; result[ index ] === result[ index - 1 ] ) result[ index - 1 ] = ""; } result = result.join( "" ); if ( noVowels ) result = result.replace( /a|e|i|o|u|y/g, "" ); if ( alpha ) result = result.replace( /[^a-z]/g, "" ); return result.trim( "neat" ); // exclude everything other than basic sentence characters eg excluding punctuation }, /* * documented in HTMLencode() */ /** * performs a simple check to determine if the string looks like HTML * @function isHTML() * @returns {boolean} **true** if the string looks like HTML * @example "sdfdgs".wider_isHTML() // false * "&lt;div>sdfdgs&lt;/div>".wider_isHTML() // true */ wider_isHTML: function () { return /^\s*&lt;[\s\S]*>\s*$/.test( this ); }, /** * Amends the parameters to the standard javascript `.replace()` so that the characters normally interpreted in the newValue as replacement instructions are left as they are * if you need the same protection on the pattern then