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 27var regExpChars = /[|\\{}()[\]^$+*?.]/g; 28var hasOwnProperty = Object.prototype.hasOwnProperty; 29var 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 */ 41exports.escapeRegExpChars = function (string) { 42 // istanbul ignore if 43 if (!string) { 44 return ''; 45 } 46 return String(string).replace(regExpChars, '\\$&'); 47}; 48 49var _ENCODE_HTML_RULES = { 50 '&': '&', 51 '<': '<', 52 '>': '>', 53 '"': '"', 54 "'": ''' 55}; 56var _MATCH_HTML = /[&<>'"]/g; 57 58function 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 71var 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 96exports.escapeXML = function (markup) { 97 return markup == undefined 98 ? '' 99 : String(markup) 100 .replace(_MATCH_HTML, encode_char); 101}; 102 103function escapeXMLToString() { 104 return Function.prototype.toString.call(this) + ';\n' + escapeFuncStr; 105} 106 107try { 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 */ 133exports.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 */ 161exports.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 */ 189exports.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 */ 213exports.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 */ 224exports.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 242