| 1 | /*
|
|---|
| 2 | * EJS Embedded JavaScript templates
|
|---|
| 3 | * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
|
|---|
| 4 | *
|
|---|
| 5 | * Licensed under the Apache License, Version 2.0 (the "License");
|
|---|
| 6 | * you may not use this file except in compliance with the License.
|
|---|
| 7 | * You may obtain a copy of the License at
|
|---|
| 8 | *
|
|---|
| 9 | * http://www.apache.org/licenses/LICENSE-2.0
|
|---|
| 10 | *
|
|---|
| 11 | * Unless required by applicable law or agreed to in writing, software
|
|---|
| 12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
|---|
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|---|
| 14 | * See the License for the specific language governing permissions and
|
|---|
| 15 | * limitations under the License.
|
|---|
| 16 | *
|
|---|
| 17 | */
|
|---|
| 18 |
|
|---|
| 19 | /**
|
|---|
| 20 | * Private utility functions
|
|---|
| 21 | * @module utils
|
|---|
| 22 | * @private
|
|---|
| 23 | */
|
|---|
| 24 |
|
|---|
| 25 | 'use strict';
|
|---|
| 26 |
|
|---|
| 27 | var regExpChars = /[|\\{}()[\]^$+*?.]/g;
|
|---|
| 28 | var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|---|
| 29 | var hasOwn = function (obj, key) { return hasOwnProperty.apply(obj, [key]); };
|
|---|
| 30 |
|
|---|
| 31 | /**
|
|---|
| 32 | * Escape characters reserved in regular expressions.
|
|---|
| 33 | *
|
|---|
| 34 | * If `string` is `undefined` or `null`, the empty string is returned.
|
|---|
| 35 | *
|
|---|
| 36 | * @param {String} string Input string
|
|---|
| 37 | * @return {String} Escaped string
|
|---|
| 38 | * @static
|
|---|
| 39 | * @private
|
|---|
| 40 | */
|
|---|
| 41 | exports.escapeRegExpChars = function (string) {
|
|---|
| 42 | // istanbul ignore if
|
|---|
| 43 | if (!string) {
|
|---|
| 44 | return '';
|
|---|
| 45 | }
|
|---|
| 46 | return String(string).replace(regExpChars, '\\$&');
|
|---|
| 47 | };
|
|---|
| 48 |
|
|---|
| 49 | var _ENCODE_HTML_RULES = {
|
|---|
| 50 | '&': '&',
|
|---|
| 51 | '<': '<',
|
|---|
| 52 | '>': '>',
|
|---|
| 53 | '"': '"',
|
|---|
| 54 | "'": '''
|
|---|
| 55 | };
|
|---|
| 56 | var _MATCH_HTML = /[&<>'"]/g;
|
|---|
| 57 |
|
|---|
| 58 | function encode_char(c) {
|
|---|
| 59 | return _ENCODE_HTML_RULES[c] || c;
|
|---|
| 60 | }
|
|---|
| 61 |
|
|---|
| 62 | /**
|
|---|
| 63 | * Stringified version of constants used by {@link module:utils.escapeXML}.
|
|---|
| 64 | *
|
|---|
| 65 | * It is used in the process of generating {@link ClientFunction}s.
|
|---|
| 66 | *
|
|---|
| 67 | * @readonly
|
|---|
| 68 | * @type {String}
|
|---|
| 69 | */
|
|---|
| 70 |
|
|---|
| 71 | var escapeFuncStr =
|
|---|
| 72 | 'var _ENCODE_HTML_RULES = {\n'
|
|---|
| 73 | + ' "&": "&"\n'
|
|---|
| 74 | + ' , "<": "<"\n'
|
|---|
| 75 | + ' , ">": ">"\n'
|
|---|
| 76 | + ' , \'"\': """\n'
|
|---|
| 77 | + ' , "\'": "'"\n'
|
|---|
| 78 | + ' }\n'
|
|---|
| 79 | + ' , _MATCH_HTML = /[&<>\'"]/g;\n'
|
|---|
| 80 | + 'function encode_char(c) {\n'
|
|---|
| 81 | + ' return _ENCODE_HTML_RULES[c] || c;\n'
|
|---|
| 82 | + '};\n';
|
|---|
| 83 |
|
|---|
| 84 | /**
|
|---|
| 85 | * Escape characters reserved in XML.
|
|---|
| 86 | *
|
|---|
| 87 | * If `markup` is `undefined` or `null`, the empty string is returned.
|
|---|
| 88 | *
|
|---|
| 89 | * @implements {EscapeCallback}
|
|---|
| 90 | * @param {String} markup Input string
|
|---|
| 91 | * @return {String} Escaped string
|
|---|
| 92 | * @static
|
|---|
| 93 | * @private
|
|---|
| 94 | */
|
|---|
| 95 |
|
|---|
| 96 | exports.escapeXML = function (markup) {
|
|---|
| 97 | return markup == undefined
|
|---|
| 98 | ? ''
|
|---|
| 99 | : String(markup)
|
|---|
| 100 | .replace(_MATCH_HTML, encode_char);
|
|---|
| 101 | };
|
|---|
| 102 |
|
|---|
| 103 | function escapeXMLToString() {
|
|---|
| 104 | return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr;
|
|---|
| 105 | }
|
|---|
| 106 |
|
|---|
| 107 | try {
|
|---|
| 108 | if (typeof Object.defineProperty === 'function') {
|
|---|
| 109 | // If the Function prototype is frozen, the "toString" property is non-writable. This means that any objects which inherit this property
|
|---|
| 110 | // cannot have the property changed using an assignment. If using strict mode, attempting that will cause an error. If not using strict
|
|---|
| 111 | // mode, attempting that will be silently ignored.
|
|---|
| 112 | // However, we can still explicitly shadow the prototype's "toString" property by defining a new "toString" property on this object.
|
|---|
| 113 | Object.defineProperty(exports.escapeXML, 'toString', { value: escapeXMLToString });
|
|---|
| 114 | } else {
|
|---|
| 115 | // If Object.defineProperty() doesn't exist, attempt to shadow this property using the assignment operator.
|
|---|
| 116 | exports.escapeXML.toString = escapeXMLToString;
|
|---|
| 117 | }
|
|---|
| 118 | } catch (err) {
|
|---|
| 119 | console.warn('Unable to set escapeXML.toString (is the Function prototype frozen?)');
|
|---|
| 120 | }
|
|---|
| 121 |
|
|---|
| 122 | /**
|
|---|
| 123 | * Naive copy of properties from one object to another.
|
|---|
| 124 | * Does not recurse into non-scalar properties
|
|---|
| 125 | * Does not check to see if the property has a value before copying
|
|---|
| 126 | *
|
|---|
| 127 | * @param {Object} to Destination object
|
|---|
| 128 | * @param {Object} from Source object
|
|---|
| 129 | * @return {Object} Destination object
|
|---|
| 130 | * @static
|
|---|
| 131 | * @private
|
|---|
| 132 | */
|
|---|
| 133 | exports.shallowCopy = function (to, from) {
|
|---|
| 134 | from = from || {};
|
|---|
| 135 | if ((to !== null) && (to !== undefined)) {
|
|---|
| 136 | for (var p in from) {
|
|---|
| 137 | if (!hasOwn(from, p)) {
|
|---|
| 138 | continue;
|
|---|
| 139 | }
|
|---|
| 140 | if (p === '__proto__' || p === 'constructor') {
|
|---|
| 141 | continue;
|
|---|
| 142 | }
|
|---|
| 143 | to[p] = from[p];
|
|---|
| 144 | }
|
|---|
| 145 | }
|
|---|
| 146 | return to;
|
|---|
| 147 | };
|
|---|
| 148 |
|
|---|
| 149 | /**
|
|---|
| 150 | * Naive copy of a list of key names, from one object to another.
|
|---|
| 151 | * Only copies property if it is actually defined
|
|---|
| 152 | * Does not recurse into non-scalar properties
|
|---|
| 153 | *
|
|---|
| 154 | * @param {Object} to Destination object
|
|---|
| 155 | * @param {Object} from Source object
|
|---|
| 156 | * @param {Array} list List of properties to copy
|
|---|
| 157 | * @return {Object} Destination object
|
|---|
| 158 | * @static
|
|---|
| 159 | * @private
|
|---|
| 160 | */
|
|---|
| 161 | exports.shallowCopyFromList = function (to, from, list) {
|
|---|
| 162 | list = list || [];
|
|---|
| 163 | from = from || {};
|
|---|
| 164 | if ((to !== null) && (to !== undefined)) {
|
|---|
| 165 | for (var i = 0; i < list.length; i++) {
|
|---|
| 166 | var p = list[i];
|
|---|
| 167 | if (typeof from[p] != 'undefined') {
|
|---|
| 168 | if (!hasOwn(from, p)) {
|
|---|
| 169 | continue;
|
|---|
| 170 | }
|
|---|
| 171 | if (p === '__proto__' || p === 'constructor') {
|
|---|
| 172 | continue;
|
|---|
| 173 | }
|
|---|
| 174 | to[p] = from[p];
|
|---|
| 175 | }
|
|---|
| 176 | }
|
|---|
| 177 | }
|
|---|
| 178 | return to;
|
|---|
| 179 | };
|
|---|
| 180 |
|
|---|
| 181 | /**
|
|---|
| 182 | * Simple in-process cache implementation. Does not implement limits of any
|
|---|
| 183 | * sort.
|
|---|
| 184 | *
|
|---|
| 185 | * @implements {Cache}
|
|---|
| 186 | * @static
|
|---|
| 187 | * @private
|
|---|
| 188 | */
|
|---|
| 189 | exports.cache = {
|
|---|
| 190 | _data: {},
|
|---|
| 191 | set: function (key, val) {
|
|---|
| 192 | this._data[key] = val;
|
|---|
| 193 | },
|
|---|
| 194 | get: function (key) {
|
|---|
| 195 | return this._data[key];
|
|---|
| 196 | },
|
|---|
| 197 | remove: function (key) {
|
|---|
| 198 | delete this._data[key];
|
|---|
| 199 | },
|
|---|
| 200 | reset: function () {
|
|---|
| 201 | this._data = {};
|
|---|
| 202 | }
|
|---|
| 203 | };
|
|---|
| 204 |
|
|---|
| 205 | /**
|
|---|
| 206 | * Transforms hyphen case variable into camel case.
|
|---|
| 207 | *
|
|---|
| 208 | * @param {String} string Hyphen case string
|
|---|
| 209 | * @return {String} Camel case string
|
|---|
| 210 | * @static
|
|---|
| 211 | * @private
|
|---|
| 212 | */
|
|---|
| 213 | exports.hyphenToCamel = function (str) {
|
|---|
| 214 | return str.replace(/-[a-z]/g, function (match) { return match[1].toUpperCase(); });
|
|---|
| 215 | };
|
|---|
| 216 |
|
|---|
| 217 | /**
|
|---|
| 218 | * Returns a null-prototype object in runtimes that support it
|
|---|
| 219 | *
|
|---|
| 220 | * @return {Object} Object, prototype will be set to null where possible
|
|---|
| 221 | * @static
|
|---|
| 222 | * @private
|
|---|
| 223 | */
|
|---|
| 224 | exports.createNullProtoObjWherePossible = (function () {
|
|---|
| 225 | if (typeof Object.create == 'function') {
|
|---|
| 226 | return function () {
|
|---|
| 227 | return Object.create(null);
|
|---|
| 228 | };
|
|---|
| 229 | }
|
|---|
| 230 | if (!({__proto__: null} instanceof Object)) {
|
|---|
| 231 | return function () {
|
|---|
| 232 | return {__proto__: null};
|
|---|
| 233 | };
|
|---|
| 234 | }
|
|---|
| 235 | // Not possible, just pass through
|
|---|
| 236 | return function () {
|
|---|
| 237 | return {};
|
|---|
| 238 | };
|
|---|
| 239 | })();
|
|---|
| 240 |
|
|---|
| 241 | exports.hasOwnOnlyObject = function (obj) {
|
|---|
| 242 | var o = exports.createNullProtoObjWherePossible();
|
|---|
| 243 | for (var p in obj) {
|
|---|
| 244 | if (hasOwn(obj, p)) {
|
|---|
| 245 | o[p] = obj[p];
|
|---|
| 246 | }
|
|---|
| 247 | }
|
|---|
| 248 | return o;
|
|---|
| 249 | };
|
|---|
| 250 |
|
|---|