PageRenderTime 60ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/release/latest/js/JSV.js

http://github.com/ServiceStack/ServiceStack
JavaScript | 588 lines | 517 code | 40 blank | 31 comment | 107 complexity | ac6f81d8d0dcc094de0ff30e5939e123 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /**
  2. * Created by IntelliJ IDEA.
  3. * User: mythz
  4. * Date: 16-Jun-2010
  5. * Time: 00:51:17
  6. * To change this template use File | Settings | File Templates.
  7. */
  8. var JSV = {};
  9. /**
  10. * parses JSV text into a JavaScript type
  11. * @param str
  12. */
  13. JSV.parse = function(str)
  14. {
  15. if (!str) return str;
  16. if (str[0] == '{')
  17. {
  18. return JSV.parseObject_(str);
  19. }
  20. else if (str[0] == '[')
  21. {
  22. return JSV.parseArray_(str);
  23. }
  24. else
  25. {
  26. return JSV.parseString(str);
  27. }
  28. }
  29. JSV.ESCAPE_CHARS = ['"', ',', '{', '}', '[', ']'];
  30. JSV.parseArray_ = function(str)
  31. {
  32. var to = [], value = JSV.stripList_(str);
  33. if (!value) return to;
  34. if (value[0] == '{')
  35. {
  36. var ref = {i:0};
  37. do
  38. {
  39. var itemValue = JSV.eatMapValue_(value, ref);
  40. to.push(JSV.parse(itemValue));
  41. } while (++ref.i < value.length);
  42. }
  43. else
  44. {
  45. for (var ref={i:0}; ref.i < value.length; ref.i++)
  46. {
  47. var elementValue = JSV.eatElementValue_(value, ref);
  48. to.push(JSV.parse(elementValue));
  49. }
  50. }
  51. return to;
  52. };
  53. JSV.parseObject_ = function(str)
  54. {
  55. if (str[0] != '{')
  56. {
  57. throw "Type definitions should start with a '{', got string starting with: "
  58. + str.substr(0, str.length < 50 ? str.length : 50);
  59. }
  60. var name, obj = {};
  61. if (str == '{}') return null;
  62. for (var ref={i:1}, strTypeLength = str.length; ref.i < strTypeLength; ref.i++)
  63. {
  64. name = JSV.eatMapKey_(str, ref);
  65. ref.i++;
  66. var value = JSV.eatMapValue_(str, ref);
  67. obj[name]= JSV.parse(value);
  68. }
  69. return obj;
  70. }
  71. JSV.eatElementValue_ = function(value, ref)
  72. {
  73. return JSV.eatUntilCharFound_(value, ref, ',');
  74. }
  75. JSV.containsAny_ = function(str, tests)
  76. {
  77. if (!is.String(str)) return;
  78. for (var i = 0, len = tests.length; i < len; i++)
  79. {
  80. if (str.indexOf(tests[i]) != -1) return true;
  81. }
  82. return false;
  83. };
  84. JSV.toCsvField = function(text)
  85. {
  86. return !text || JSV.containsAny_(JSV.ESCAPE_CHARS)
  87. ? text
  88. : '"' + text.replace(/"/g, '""') + '"';
  89. }
  90. JSV.parseString = JSV.fromCsvField = function(text)
  91. {
  92. return !text || text[0] != '"'
  93. ? text
  94. : text.substr(1, text.length - 2).replace(/""/g, '"');
  95. }
  96. JSV.stripList_ = function(value)
  97. {
  98. if (!value) return null;
  99. return value[0] == '['
  100. ? value.substr(1, value.length - 2)
  101. : value;
  102. };
  103. /**
  104. * @param value {string}
  105. * @param ref {ref int}
  106. * @param findChar {char}
  107. */
  108. JSV.eatUntilCharFound_ = function(value, ref, findChar)
  109. {
  110. var tokenStartPos = ref.i;
  111. var valueLength = value.length;
  112. if (value[tokenStartPos] != '"')
  113. {
  114. ref.i = value.indexOf(findChar, tokenStartPos);
  115. if (ref.i == -1) ref.i = valueLength;
  116. return value.substr(tokenStartPos, ref.i - tokenStartPos);
  117. }
  118. while (++ref.i < valueLength)
  119. {
  120. if (value[ref.i] == '"')
  121. {
  122. if (ref.i + 1 >= valueLength)
  123. {
  124. return value.substr(tokenStartPos, ++ref.i - tokenStartPos);
  125. }
  126. if (value[ref.i + 1] == '"')
  127. {
  128. ref.i++;
  129. }
  130. else if (value[ref.i + 1] == findChar)
  131. {
  132. return value.substr(tokenStartPos, ++ref.i - tokenStartPos);
  133. }
  134. }
  135. }
  136. throw "Could not find ending quote";
  137. }
  138. /**
  139. *
  140. * @param value {string}
  141. * @param i {ref int}
  142. */
  143. JSV.eatMapKey_ = function(value, ref)
  144. {
  145. var tokenStartPos = ref.i;
  146. while (value[++ref.i] != ':' && ref.i < value.length) { }
  147. return value.substr(tokenStartPos, ref.i - tokenStartPos);
  148. }
  149. /**
  150. *
  151. * @param value {string}
  152. * @param ref {ref int}
  153. */
  154. JSV.eatMapValue_ = function(value, ref)
  155. {
  156. var tokenStartPos = ref.i;
  157. var valueLength = value.length;
  158. if (ref.i == valueLength) return null;
  159. var valueChar = value[ref.i];
  160. //If we are at the end, return.
  161. if (valueChar == ',' || valueChar == '}')
  162. {
  163. return null;
  164. }
  165. //Is List, i.e. [...]
  166. var withinQuotes = false;
  167. if (valueChar == '[')
  168. {
  169. var endsToEat = 1;
  170. while (++ref.i < valueLength && endsToEat > 0)
  171. {
  172. valueChar = value[ref.i];
  173. if (valueChar == '"')
  174. withinQuotes = !withinQuotes;
  175. if (withinQuotes)
  176. continue;
  177. if (valueChar == '[')
  178. endsToEat++;
  179. if (valueChar == ']')
  180. endsToEat--;
  181. }
  182. return value.substr(tokenStartPos, ref.i - tokenStartPos);
  183. }
  184. //Is Type/Map, i.e. {...}
  185. if (valueChar == '{')
  186. {
  187. var endsToEat = 1;
  188. while (++ref.i < valueLength && endsToEat > 0)
  189. {
  190. valueChar = value[ref.i];
  191. if (valueChar == '"')
  192. withinQuotes = !withinQuotes;
  193. if (withinQuotes)
  194. continue;
  195. if (valueChar == '{')
  196. endsToEat++;
  197. if (valueChar == '}')
  198. endsToEat--;
  199. }
  200. return value.substr(tokenStartPos, ref.i - tokenStartPos);
  201. }
  202. //Is Within Quotes, i.e. "..."
  203. if (valueChar == '"')
  204. {
  205. while (++ref.i < valueLength)
  206. {
  207. valueChar = value[ref.i];
  208. if (valueChar != '"') continue;
  209. var isLiteralQuote = ref.i + 1 < valueLength && value[ref.i + 1] == '"';
  210. ref.i++; //skip quote
  211. if (!isLiteralQuote)
  212. break;
  213. }
  214. return value.substr(tokenStartPos, ref.i - tokenStartPos);
  215. }
  216. //Is Value
  217. while (++ref.i < valueLength)
  218. {
  219. valueChar = value[ref.i];
  220. if (valueChar == ',' || valueChar == '}')
  221. break;
  222. }
  223. return value.substr(tokenStartPos, ref.i - tokenStartPos);
  224. }
  225. JSV.isEmpty_ = function(a)
  226. {
  227. return (a === null || a === undefined || a === "");
  228. }
  229. JSV.isFunction_ = function(a)
  230. {
  231. return (typeof (a) === 'function') ? a.constructor.toString().match(/Function/) !== null : false;
  232. };
  233. JSV.isString_ = function(a)
  234. {
  235. if (a === null || a === undefined) return false;
  236. return (typeof (a) === 'string') ? true : (typeof (a) === 'object') ? a.constructor.toString().match(/string/i) !== null : false;
  237. };
  238. JSV.isDate_ = function(a)
  239. {
  240. if (JSV.isEmpty_(a)) return false;
  241. return (typeof (a) === 'date') ? true : (typeof (a) === 'object') ? a.constructor.toString().match(/date/i) !== null : false;
  242. };
  243. JSV.isArray_ = function(a)
  244. {
  245. if (a === null || a === undefined || a === "") return false;
  246. return (typeof (a) === 'object') ? a.constructor.toString().match(/array/i) !== null || a.length !== undefined : false;
  247. };
  248. JSV.toXsdDateTime = function(date)
  249. {
  250. function pad(n) {
  251. var s = n.toString();
  252. return s.length < 2 ? '0'+s : s;
  253. };
  254. var yyyy = date.getUTCFullYear();
  255. var MM = pad(date.getUTCMonth()+1);
  256. var dd = pad(date.getUTCDate());
  257. var hh = pad(date.getUTCHours());
  258. var mm = pad(date.getUTCMinutes());
  259. var ss = pad(date.getUTCSeconds());
  260. var ms = pad(date.getUTCMilliseconds());
  261. return yyyy +'-' + MM + '-' + dd + 'T' + hh + ':' + mm + ':' + ss + '.' + ms + 'Z';
  262. }
  263. JSV.serialize = JSV.stringify = function(obj)
  264. {
  265. if (obj === null || obj === undefined) return null;
  266. var typeOf = typeof(obj);
  267. if (obj === 'function') return null;
  268. if (typeOf === 'object')
  269. {
  270. var ctorStr = obj.constructor.toString().toLowerCase();
  271. if (ctorStr.indexOf('string') != -1)
  272. return JSV.escapeString(obj);
  273. if (ctorStr.indexOf('boolean') != -1)
  274. return obj ? "True" : "False";
  275. if (ctorStr.indexOf('number') != -1)
  276. return obj;
  277. if (ctorStr.indexOf('date') != -1)
  278. return JSV.toXsdDateTime(obj);
  279. if (ctorStr.indexOf('array') != -1)
  280. return JSV.serializeArray(obj);
  281. return JSV.serializeObject(obj);
  282. }
  283. else
  284. {
  285. switch(typeOf)
  286. {
  287. case 'string':
  288. return JSV.escapeString(obj);
  289. break;
  290. case 'boolean':
  291. return obj ? "True" : "False";
  292. break;
  293. case 'date':
  294. return JSV.toXsdDateTime(obj);
  295. break;
  296. case 'array':
  297. return JSV.serializeArray(obj);
  298. break;
  299. case 'number':
  300. default:
  301. return obj;
  302. }
  303. }
  304. };
  305. JSV.serializeObject = function(obj)
  306. {
  307. var value, sb = new StringBuffer();
  308. for (var key in obj)
  309. {
  310. value = obj[key];
  311. if (!obj.hasOwnProperty(key) || JSV.isEmpty_(value) || JSV.isFunction_(value)) continue;
  312. if (sb.length > 0)
  313. sb.append(',');
  314. sb.append(JSV.escapeString(key));
  315. sb.append(':');
  316. sb.append(JSV.serialize(value));
  317. }
  318. return '{' + sb.toString() + '}';
  319. };
  320. JSV.serializeArray = function(array)
  321. {
  322. var value, sb = new StringBuffer();
  323. for (var i=0, len=array.length; i<len; i++)
  324. {
  325. value = array[i];
  326. if (JSV.isEmpty_(value) || JSV.isFunction_(value)) continue;
  327. if (sb.getLength() > 0)
  328. sb.append(',');
  329. sb.append(JSV.serialize(value));
  330. }
  331. return '[' + sb.toString() + ']';
  332. };
  333. JSV.escapeString = function(str)
  334. {
  335. if (str === undefined || str === null) return null;
  336. if (str === '') return '""';
  337. if (str.indexOf('"'))
  338. {
  339. str = str.replace(/"/g,'""');
  340. }
  341. if (JSV.containsAny_(str, JSV.ESCAPE_CHARS))
  342. {
  343. return '"' + str + '"';
  344. }
  345. return str;
  346. };
  347. JSV.containsAny_ = function(str, tests)
  348. {
  349. if (!JSV.isString_(str)) return;
  350. for (var i = 0, len = tests.length; i < len; i++)
  351. {
  352. if (str.indexOf(tests[i]) != -1) return true;
  353. }
  354. return false;
  355. };
  356. /* Closure Library StringBuffer for efficient string concatenation */
  357. var hasScriptEngine = 'ScriptEngine' in window;
  358. var HAS_JSCRIPT = hasScriptEngine && window['ScriptEngine']() == 'JScript';
  359. StringBuffer = function(opt_a1, var_args) {
  360. this.buffer_ = HAS_JSCRIPT ? [] : '';
  361. if (opt_a1 != null) {
  362. this.append.apply(this, arguments);
  363. }
  364. };
  365. StringBuffer.prototype.set = function(s) {
  366. this.clear();
  367. this.append(s);
  368. };
  369. if (HAS_JSCRIPT) {
  370. StringBuffer.prototype.bufferLength_ = 0;
  371. StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
  372. // IE version.
  373. if (opt_a2 == null) { // second argument is undefined (null == undefined)
  374. // Array assignment is 2x faster than Array push. Also, use a1
  375. // directly to avoid arguments instantiation, another 2x improvement.
  376. this.buffer_[this.bufferLength_++] = a1;
  377. } else {
  378. this.buffer_.push.apply(/** @type {Array} */ (this.buffer_), arguments);
  379. this.bufferLength_ = this.buffer_.length;
  380. }
  381. return this;
  382. };
  383. } else {
  384. StringBuffer.prototype.append = function(a1, opt_a2, var_args) {
  385. // W3 version.
  386. this.buffer_ += a1;
  387. if (opt_a2 != null) { // second argument is undefined (null == undefined)
  388. for (var i = 1; i < arguments.length; i++) {
  389. this.buffer_ += arguments[i];
  390. }
  391. }
  392. return this;
  393. };
  394. }
  395. StringBuffer.prototype.clear = function() {
  396. if (HAS_JSCRIPT) {
  397. this.buffer_.length = 0; // Reuse the array to avoid creating new object.
  398. this.bufferLength_ = 0;
  399. } else {
  400. this.buffer_ = '';
  401. }
  402. };
  403. StringBuffer.prototype.getLength = function() {
  404. return this.toString().length;
  405. };
  406. StringBuffer.prototype.toString = function() {
  407. if (HAS_JSCRIPT) {
  408. var str = this.buffer_.join('');
  409. this.clear();
  410. if (str) {
  411. this.append(str);
  412. }
  413. return str;
  414. } else {
  415. return /** @type {string} */ (this.buffer_);
  416. }
  417. };
  418. /**
  419. * Considering pulling this out
  420. * @param baseUri
  421. * @param type
  422. */
  423. function JsvServiceClient(baseUri)
  424. {
  425. this.baseSyncReplyUri = JsvServiceClient.combine_(baseUri, "Jsv/SyncReply");
  426. this.baseAsyncOneWayUri = JsvServiceClient.combine_(baseUri, "Jsv/AsyncOneWay");
  427. }
  428. JsvServiceClient.prototype.send = function(webMethod, request, onSuccess, onError, ajaxOptions) {
  429. var startCallTime = new Date();
  430. var requestUrl = JsvServiceClient.combine_(this.baseSyncReplyUri, webMethod);
  431. var id = JsvServiceClient.id++;
  432. var options = {
  433. type: "GET",
  434. url: requestUrl,
  435. data: request,
  436. dataType: "text",
  437. success: function(responseText)
  438. {
  439. var endCallTime = new Date();
  440. var callDuration = endCallTime.getTime() - startCallTime.getTime();
  441. var response = JSV.parse(responseText);
  442. if (!response)
  443. {
  444. if (onSuccess) onSuccess(null);
  445. return;
  446. }
  447. var status = JsvServiceClient.parseResponseStatus_(response.ResponseStatus);
  448. if (status.isSuccess)
  449. {
  450. if (onSuccess) onSuccess(response);
  451. JsvServiceClient.onSuccess({ id: id, webMethod: webMethod, request: request,
  452. response: response, durationMs: callDuration
  453. });
  454. }
  455. else
  456. {
  457. if (onError) onError(status);
  458. JsvServiceClient.onError({ id: id, webMethod: webMethod, request: request,
  459. error: status, durationMs: callDuration
  460. });
  461. }
  462. },
  463. error: function(xhr, desc, exObj)
  464. {
  465. var endCallTime = new Date();
  466. var callDuration = endCallTime.getTime() - startCallTime.getTime();
  467. try
  468. {
  469. if (onError) onError(xhr.responseText);
  470. }
  471. catch (e) {}
  472. JsvServiceClient.onError({ id: id, webMethod: webMethod, request: request,
  473. error: xhr.responseText, durationMs: callDuration
  474. });
  475. }
  476. };
  477. for (var k in ajaxOptions) options[k] = ajaxOptions[k];
  478. var ajax = $.ajax(options);
  479. };
  480. JsvServiceClient.combine_ = function() {
  481. var paths = "";
  482. for (var i = 0, len = arguments.length; i < len; i++) {
  483. if (paths.length > 0)
  484. paths += "/";
  485. paths += arguments[i].replace(/[/]+$/g, "");
  486. }
  487. return paths;
  488. };
  489. //Sends a HTTP 'GET' request on the QueryString
  490. JsvServiceClient.prototype.getFromService = function(webMethod, request, onSuccess, onError) {
  491. this.send(webMethod, request, onSuccess, onError);
  492. };
  493. //Sends a HTTP 'POST' request as key value pair formData
  494. JsvServiceClient.prototype.postFormDataToService = function(webMethod, request, onSuccess, onError) {
  495. this.send(webMethod, request, onSuccess, onError, { type: "POST" });
  496. };
  497. //Sends a HTTP 'POST' request as JSV @requires jQuery
  498. JsvServiceClient.prototype.postToService = function(webMethod, request, onSuccess, onError) {
  499. var jsvRequest = JSV.serialize(request);
  500. this.send(webMethod, jsvRequest, onSuccess, onError, { type: "POST", processData: false, contentType: "application/jsv; charset=utf-8" });
  501. };
  502. JsvServiceClient.id = 0;
  503. JsvServiceClient.onError = function() { };
  504. JsvServiceClient.onSuccess = function() { };
  505. JsvServiceClient.parseResponseStatus_ = function(status)
  506. {
  507. if (!status) return {isSuccess:true};
  508. var result =
  509. {
  510. isSuccess: status.ErrorCode === undefined || status.ErrorCode === null,
  511. errorCode: status.ErrorCode,
  512. message: status.Message,
  513. errorMessage: status.ErrorMessage,
  514. stackTrace: status.StackTrace,
  515. fieldErrors: [],
  516. fieldErrorMap: {}
  517. };
  518. if (status.FieldErrors)
  519. {
  520. for (var i=0, len = status.FieldErrors.length; i<len; i++)
  521. {
  522. var err = status.FieldErrors[i];
  523. var error = {errorCode: err.ErrorCode, fieldName:err.FieldName, errorMessage:err.ErrorMessage||''};
  524. result.fieldErrors.push(error);
  525. if (error.fieldName)
  526. {
  527. result.fieldErrorMap[error.fieldName] = error;
  528. }
  529. }
  530. }
  531. return result;
  532. };