helpers.js 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. export const logging = assign({}, console);
  2. export const NS_HTML = 'http://www.w3.org/1999/xhtml';
  3. /** When looking for documentElement, use '*' to also support XML pages */
  4. export const elemByTag = (tag, i) => document::getElementsByTagName(tag)[i || 0];
  5. // Firefox defines `isFinite` on `global` not on `window`
  6. const { isFinite } = global; // eslint-disable-line no-restricted-properties
  7. const { toString: numberToString } = 0;
  8. const isArray = obj => (
  9. // ES3 way, not reliable if prototype is modified
  10. // Object.prototype.toString.call(obj) === '[object Array]'
  11. // #565 steamcommunity.com has overridden `Array.prototype`
  12. // support duck typing
  13. obj && typeof obj.length === 'number' && typeof obj.splice === 'function'
  14. );
  15. // Reference: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/JSON#Polyfill
  16. const escMap = {
  17. '"': '\\"',
  18. '\\': '\\\\',
  19. '\b': '\\b',
  20. '\f': '\\f',
  21. '\n': '\\n',
  22. '\r': '\\r',
  23. '\t': '\\t',
  24. };
  25. const escRE = /[\\"\u0000-\u001F\u2028\u2029]/g; // eslint-disable-line no-control-regex
  26. const escFunc = m => escMap[m] || `\\u${(m::charCodeAt(0) + 0x10000)::numberToString(16)::slice(1)}`;
  27. // When running in the page context we must beware of sites that override Array#toJSON
  28. // leading to an invalid result, which is why our jsonDump() ignores toJSON.
  29. // Thus, we use the native JSON.stringify() only in the content script context and only until
  30. // a userscript is injected into this context (due to `@inject-into` and/or a CSP problem).
  31. export function jsonDump(value) {
  32. if (value == null) return 'null';
  33. const type = typeof value;
  34. if (type === 'number') return isFinite(value) ? `${value}` : 'null';
  35. if (type === 'boolean') return `${value}`;
  36. if (type === 'object') {
  37. if (isArray(value)) {
  38. return `[${value::map(jsonDump)::join(',')}]`;
  39. }
  40. if (value::objectToString() === '[object Object]') {
  41. const res = objectKeys(value)::map((key) => {
  42. const v = value[key];
  43. return v !== undefined && `${jsonDump(key)}:${jsonDump(v)}`;
  44. });
  45. // JSON.stringify skips undefined in objects i.e. {foo: undefined} produces {}
  46. return `{${res::filter(Boolean)::join(',')}}`;
  47. }
  48. }
  49. return `"${value::replace(escRE, escFunc)}"`;
  50. }
  51. /** args is [tags?, ...rest] */
  52. export function log(level, ...args) {
  53. args[0] = `[Violentmonkey]${args[0] ? `[${args[0]::join('][')}]` : ''}`;
  54. logging[level]::apply(logging, args);
  55. }