Advertisement
Guest User

Untitled

a guest
Jan 20th, 2017
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.45 KB | None | 0 0
  1. (function(){
  2. // do not move this outside of the scope.
  3. "use strict";
  4.  
  5. // default options
  6. var defaultOptions = {
  7.  
  8. // if true, allow null values
  9. allowNull: false,
  10.  
  11. // if true, attempt to convert non-matching types implicitly
  12. implicitConversion: true,
  13.  
  14. // if true, apply property access strictness via a proxy
  15. proxy: true
  16. };
  17.  
  18. // default values
  19. var defaultValues = {
  20. Number: 0,
  21. String: ""
  22. };
  23.  
  24. // overwrite target fields with source fields that exist
  25. function apply(target, source) {
  26. if (target && source) {
  27. for (var i in source) {
  28. target[i] = source[i];
  29. }
  30. }
  31. return target;
  32. }
  33.  
  34. // create constructor
  35. function construct(options) {
  36.  
  37. // determine what was passed to the constructor
  38. options = parseJson(options);
  39. if (!options.schema) {
  40. options = { schema: options };
  41. }
  42.  
  43. // need a schema to continue
  44. if (!options.schema) {
  45. throw "No schema specified.";
  46. }
  47.  
  48. // apply default options
  49. options = apply(apply({}, defaultOptions), options);
  50.  
  51. // build proxy handler
  52. var proxyHandler = constructProxyHandler(options);
  53.  
  54. // create base constructor
  55. return function(data) {
  56. var result = {};
  57.  
  58. // assume incoming strings are raw JSON
  59. data = apply({}, parseJson(data));
  60.  
  61. // create properties
  62. for (var propertyName in options.schema) {
  63. var propertyType = options.schema[propertyName].name;
  64.  
  65. Object.defineProperty(
  66. result,
  67. propertyName,
  68. constructAccessors(propertyName, options)
  69. );
  70.  
  71. // assign defaults if nulls are not allowed
  72. if (!options.allowNull) {
  73. if (!(propertyName in data) && exists(defaultValues[propertyType])) {
  74. result[propertyName] = defaultValues[propertyType];
  75. }
  76. }
  77. }
  78.  
  79. // assign initial values
  80. for (var presetName in data) {
  81. result[presetName] = data[presetName];
  82. }
  83.  
  84. // make the properties visible to JSON.stringify
  85. result.toJSON = constructJsonHandler(options);
  86.  
  87. // attach a proxy, if the feature is available
  88. if (typeof Proxy == "function") {
  89. return new Proxy(result, proxyHandler);
  90. } else {
  91. return result;
  92. }
  93. };
  94. }
  95.  
  96. // create getter+setter pair
  97. function constructAccessors(name, options) {
  98. var value;
  99.  
  100. return {
  101. get: function() { return value; },
  102. set: constructSetter(
  103. name,
  104. options,
  105. function(newValue) { value = newValue; }
  106. )
  107. }
  108. }
  109.  
  110. // create toJSON handler
  111. function constructJsonHandler(options) {
  112. return function() {
  113. var result = {};
  114. for (var field in options.schema) {
  115. result[field] = this[field];
  116. }
  117. return result;
  118. };
  119. }
  120.  
  121. // create a proxy handler for object constructors (ES6)
  122. function constructProxyHandler(options) {
  123. return {
  124. // prevent deletion of properties in the schema
  125. deleteProperty: function(target, property) {
  126. if (!(property in options.schema)) {
  127. return delete target[property];
  128. }
  129. throw "Can't delete property '" + property + "': it is enforced in the schema.";
  130. },
  131. };
  132. }
  133.  
  134. // create setter
  135. function constructSetter(name, options, setFunc) {
  136.  
  137. // cache these values for performance
  138. var allowNull = options.allowNull;
  139. var fieldType = options.schema[name];
  140. var implicit = options.implicitConversion;
  141.  
  142. // build the setter
  143. return function(value) {
  144. if (!exists(value)) {
  145. value = null;
  146. }
  147. if (value !== null || value === null && allowNull) {
  148. if (value !== null) {
  149. if (implicit && value.constructor !== fieldType) {
  150. value = convert(value, fieldType);
  151. }
  152. if (value.constructor === fieldType) {
  153. setFunc(value);
  154. } else {
  155. throw "Can't store " + value.constructor.name + " in '" + name + "'.";
  156. }
  157. }
  158. } else {
  159. throw "Can't store null in '" + name + "'.";
  160. }
  161. };
  162. }
  163.  
  164. // used for implicit conversion
  165. function convert(value, newType) {
  166.  
  167. // null and undefined convert specially
  168. if (value === null || !exists(value)) {
  169. return null;
  170. }
  171. var oldType = value.constructor;
  172.  
  173. // we try to use .toString() if provided
  174. if (newType === String) {
  175. if (typeof value.toString == "function") {
  176. return value.toString();
  177. } else {
  178. return String(value);
  179. }
  180. }
  181.  
  182. // NaN is considered a failed conversion
  183. if (newType === Number) {
  184. var converted = Number(value);
  185. if (!isNaN(converted)) {
  186. return converted;
  187. }
  188. }
  189.  
  190. // if all other conversions don't work, fail
  191. throw "Can't convert " + oldType.toString() + " implicitly.";
  192. }
  193.  
  194. // returns true when an object isn't undefined
  195. function exists(obj) {
  196. return (typeof obj != "undefined");
  197. }
  198.  
  199. // parse an object out of a number of input types
  200. function parseJson(json) {
  201. if (json === null || !exists(json)) {
  202. return {};
  203. } else if (typeof json == "string") {
  204. return JSON.parse(json);
  205. } else {
  206. return json;
  207. }
  208. }
  209.  
  210. // stick it on the global context
  211. if (exists(window)) {
  212. window.Struct = construct;
  213. } else if (exists(global)) {
  214. global.Struct = construct;
  215. } else {
  216. console.warn("Couldn't really find a context to install Struct into.");
  217. }
  218.  
  219. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement