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  '<': '&lt;',
52  '>': '&gt;',
53  '"': '&#34;',
54  "'": '&#39;'
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+ '      "&": "&amp;"\n'
74+ '    , "<": "&lt;"\n'
75+ '    , ">": "&gt;"\n'
76+ '    , \'"\': "&#34;"\n'
77+ '    , "\'": "&#39;"\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