Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. /*!
  2. * validate.js 0.11.1
  3. *
  4. * (c) 2013-2016 Nicklas Ansman, 2013 Wrapp
  5. * Validate.js may be freely distributed under the MIT license.
  6. * For all details and documentation:
  7. * http://validatejs.org/
  8. */
  9. (function(exports, module, define) {
  10. "use strict";
  11. // The main function that calls the validators specified by the constraints.
  12. // The options are the following:
  13. // - format (string) - An option that controls how the returned value is formatted
  14. // * flat - Returns a flat array of just the error messages
  15. // * grouped - Returns the messages grouped by attribute (default)
  16. // * detailed - Returns an array of the raw validation data
  17. // - fullMessages (boolean) - If `true` (default) the attribute name is prepended to the error.
  18. //
  19. // Please note that the options are also passed to each validator.
  20. var validate = function(attributes, constraints, options) {
  21. options = v.extend({}, v.options, options);
  22. var results = v.runValidations(attributes, constraints, options)
  23. , attr
  24. , validator;
  25. for (attr in results) {
  26. for (validator in results[attr]) {
  27. if (v.isPromise(results[attr][validator])) {
  28. throw new Error("Use validate.async if you want support for promises");
  29. }
  30. }
  31. }
  32. return validate.processValidationResults(results, options);
  33. };
  34. var v = validate;
  35. // Copies over attributes from one or more sources to a single destination.
  36. // Very much similar to underscore's extend.
  37. // The first argument is the target object and the remaining arguments will be
  38. // used as sources.
  39. v.extend = function(obj) {
  40. [].slice.call(arguments, 1).forEach(function(source) {
  41. for (var attr in source) {
  42. obj[attr] = source[attr];
  43. }
  44. });
  45. return obj;
  46. };
  47. v.extend(validate, {
  48. // This is the version of the library as a semver.
  49. // The toString function will allow it to be coerced into a string
  50. version: {
  51. major: 0,
  52. minor: 11,
  53. patch: 1,
  54. metadata: "development",
  55. toString: function() {
  56. var version = v.format("%{major}.%{minor}.%{patch}", v.version);
  57. if (!v.isEmpty(v.version.metadata)) {
  58. version += "+" + v.version.metadata;
  59. }
  60. return version;
  61. }
  62. },
  63. // Below is the dependencies that are used in validate.js
  64. // The constructor of the Promise implementation.
  65. // If you are using Q.js, RSVP or any other A+ compatible implementation
  66. // override this attribute to be the constructor of that promise.
  67. // Since jQuery promises aren't A+ compatible they won't work.
  68. Promise: typeof Promise !== "undefined" ? Promise : /* istanbul ignore next */ null,
  69. EMPTY_STRING_REGEXP: /^\s*$/,
  70. // Runs the validators specified by the constraints object.
  71. // Will return an array of the format:
  72. // [{attribute: "<attribute name>", error: "<validation result>"}, ...]
  73. runValidations: function(attributes, constraints, options) {
  74. var results = []
  75. , attr
  76. , validatorName
  77. , value
  78. , validators
  79. , validator
  80. , validatorOptions
  81. , error;
  82. if (v.isDomElement(attributes) || v.isJqueryElement(attributes)) {
  83. attributes = v.collectFormValues(attributes);
  84. }
  85. // Loops through each constraints, finds the correct validator and run it.
  86. for (attr in constraints) {
  87. value = v.getDeepObjectValue(attributes, attr);
  88. // This allows the constraints for an attribute to be a function.
  89. // The function will be called with the value, attribute name, the complete dict of
  90. // attributes as well as the options and constraints passed in.
  91. // This is useful when you want to have different
  92. // validations depending on the attribute value.
  93. validators = v.result(constraints[attr], value, attributes, attr, options, constraints);
  94. for (validatorName in validators) {
  95. validator = v.validators[validatorName];
  96. if (!validator) {
  97. error = v.format("Unknown validator %{name}", {name: validatorName});
  98. throw new Error(error);
  99. }
  100. validatorOptions = validators[validatorName];
  101. // This allows the options to be a function. The function will be
  102. // called with the value, attribute name, the complete dict of
  103. // attributes as well as the options and constraints passed in.
  104. // This is useful when you want to have different
  105. // validations depending on the attribute value.
  106. validatorOptions = v.result(validatorOptions, value, attributes, attr, options, constraints);
  107. if (!validatorOptions) {
  108. continue;
  109. }
  110. results.push({
  111. attribute: attr,
  112. value: value,
  113. validator: validatorName,
  114. globalOptions: options,
  115. attributes: attributes,
  116. options: validatorOptions,
  117. error: validator.call(validator,
  118. value,
  119. validatorOptions,
  120. attr,
  121. attributes,
  122. options)
  123. });
  124. }
  125. }
  126. return results;
  127. },
  128. // Takes the output from runValidations and converts it to the correct
  129. // output format.
  130. processValidationResults: function(errors, options) {
  131. errors = v.pruneEmptyErrors(errors, options);
  132. errors = v.expandMultipleErrors(errors, options);
  133. errors = v.convertErrorMessages(errors, options);
  134. var format = options.format || "grouped";
  135. if (typeof v.formatters[format] === 'function') {
  136. errors = v.formatters[format](errors);
  137. } else {
  138. throw new Error(v.format("Unknown format %{format}", options));
  139. }
  140. return v.isEmpty(errors) ? undefined : errors;
  141. },
  142. // Runs the validations with support for promises.
  143. // This function will return a promise that is settled when all the
  144. // validation promises have been completed.
  145. // It can be called even if no validations returned a promise.
  146. async: function(attributes, constraints, options) {
  147. options = v.extend({}, v.async.options, options);
  148. var WrapErrors = options.wrapErrors || function(errors) {
  149. return errors;
  150. };
  151. // Removes unknown attributes
  152. if (options.cleanAttributes !== false) {
  153. attributes = v.cleanAttributes(attributes, constraints);
  154. }
  155. var results = v.runValidations(attributes, constraints, options);
  156. return new v.Promise(function(resolve, reject) {
  157. v.waitForResults(results).then(function() {
  158. var errors = v.processValidationResults(results, options);
  159. if (errors) {
  160. reject(new WrapErrors(errors, options, attributes, constraints));
  161. } else {
  162. resolve(attributes);
  163. }
  164. }, function(err) {
  165. reject(err);
  166. });
  167. });
  168. },
  169. single: function(value, constraints, options) {
  170. options = v.extend({}, v.single.options, options, {
  171. format: "flat",
  172. fullMessages: false
  173. });
  174. return v({single: value}, {single: constraints}, options);
  175. },
  176. // Returns a promise that is resolved when all promises in the results array
  177. // are settled. The promise returned from this function is always resolved,
  178. // never rejected.
  179. // This function modifies the input argument, it replaces the promises
  180. // with the value returned from the promise.
  181. waitForResults: function(results) {
  182. // Create a sequence of all the results starting with a resolved promise.
  183. return results.reduce(function(memo, result) {
  184. // If this result isn't a promise skip it in the sequence.
  185. if (!v.isPromise(result.error)) {
  186. return memo;
  187. }
  188. return memo.then(function() {
  189. return result.error.then(function(error) {
  190. result.error = error || null;
  191. });
  192. });
  193. }, new v.Promise(function(r) { r(); })); // A resolved promise
  194. },
  195. // If the given argument is a call: function the and: function return the value
  196. // otherwise just return the value. Additional arguments will be passed as
  197. // arguments to the function.
  198. // Example:
  199. // ```
  200. // result('foo') // 'foo'
  201. // result(Math.max, 1, 2) // 2
  202. // ```
  203. result: function(value) {
  204. var args = [].slice.call(arguments, 1);
  205. if (typeof value === 'function') {
  206. value = value.apply(null, args);
  207. }
  208. return value;
  209. },
  210. // Checks if the value is a number. This function does not consider NaN a
  211. // number like many other `isNumber` functions do.
  212. isNumber: function(value) {
  213. return typeof value === 'number' && !isNaN(value);
  214. },
  215. // Returns false if the object is not a function
  216. isFunction: function(value) {
  217. return typeof value === 'function';
  218. },
  219. // A simple check to verify that the value is an integer. Uses `isNumber`
  220. // and a simple modulo check.
  221. isInteger: function(value) {
  222. return v.isNumber(value) && value % 1 === 0;
  223. },
  224. // Checks if the value is a boolean
  225. isBoolean: function(value) {
  226. return typeof value === 'boolean';
  227. },
  228. // Uses the `Object` function to check if the given argument is an object.
  229. isObject: function(obj) {
  230. return obj === Object(obj);
  231. },
  232. // Simply checks if the object is an instance of a date
  233. isDate: function(obj) {
  234. return obj instanceof Date;
  235. },
  236. // Returns false if the object is `null` of `undefined`
  237. isDefined: function(obj) {
  238. return obj !== null && obj !== undefined;
  239. },
  240. // Checks if the given argument is a promise. Anything with a `then`
  241. // function is considered a promise.
  242. isPromise: function(p) {
  243. return !!p && v.isFunction(p.then);
  244. },
  245. isJqueryElement: function(o) {
  246. return o && v.isString(o.jquery);
  247. },
  248. isDomElement: function(o) {
  249. if (!o) {
  250. return false;
  251. }
  252. if (!o.querySelectorAll || !o.querySelector) {
  253. return false;
  254. }
  255. if (v.isObject(document) && o === document) {
  256. return true;
  257. }
  258. // http://stackoverflow.com/a/384380/699304
  259. /* istanbul ignore else */
  260. if (typeof HTMLElement === "object") {
  261. return o instanceof HTMLElement;
  262. } else {
  263. return o &&
  264. typeof o === "object" &&
  265. o !== null &&
  266. o.nodeType === 1 &&
  267. typeof o.nodeName === "string";
  268. }
  269. },
  270. isEmpty: function(value) {
  271. var attr;
  272. // Null and undefined are empty
  273. if (!v.isDefined(value)) {
  274. return true;
  275. }
  276. // functions are non empty
  277. if (v.isFunction(value)) {
  278. return false;
  279. }
  280. // Whitespace only strings are empty
  281. if (v.isString(value)) {
  282. return v.EMPTY_STRING_REGEXP.test(value);
  283. }
  284. // For arrays we use the length property
  285. if (v.isArray(value)) {
  286. return value.length === 0;
  287. }
  288. // Dates have no attributes but aren't empty
  289. if (v.isDate(value)) {
  290. return false;
  291. }
  292. // If we find at least one property we consider it non empty
  293. if (v.isObject(value)) {
  294. for (attr in value) {
  295. return false;
  296. }
  297. return true;
  298. }
  299. return false;
  300. },
  301. // Formats the specified strings with the given values like so:
  302. // ```
  303. // format("Foo: %{foo}", {foo: "bar"}) // "Foo bar"
  304. // ```
  305. // If you want to write %{...} without having it replaced simply
  306. // prefix it with % like this `Foo: %%{foo}` and it will be returned
  307. // as `"Foo: %{foo}"`
  308. format: v.extend(function(str, vals) {
  309. if (!v.isString(str)) {
  310. return str;
  311. }
  312. return str.replace(v.format.FORMAT_REGEXP, function(m0, m1, m2) {
  313. if (m1 === '%') {
  314. return "%{" + m2 + "}";
  315. } else {
  316. return String(vals[m2]);
  317. }
  318. });
  319. }, {
  320. // Finds %{key} style patterns in the given string
  321. FORMAT_REGEXP: /(%?)%\{([^\}]+)\}/g
  322. }),
  323. // "Prettifies" the given string.
  324. // Prettifying means replacing [.\_-] with spaces as well as splitting
  325. // camel case words.
  326. prettify: function(str) {
  327. if (v.isNumber(str)) {
  328. // If there are more than 2 decimals round it to two
  329. if ((str * 100) % 1 === 0) {
  330. return "" + str;
  331. } else {
  332. return parseFloat(Math.round(str * 100) / 100).toFixed(2);
  333. }
  334. }
  335. if (v.isArray(str)) {
  336. return str.map(function(s) { return v.prettify(s); }).join(", ");
  337. }
  338. if (v.isObject(str)) {
  339. return str.toString();
  340. }
  341. // Ensure the string is actually a string
  342. str = "" + str;
  343. return str
  344. // Splits keys separated by periods
  345. .replace(/([^\s])\.([^\s])/g, '$1 $2')
  346. // Removes backslashes
  347. .replace(/\\+/g, '')
  348. // Replaces - and - with space
  349. .replace(/[_-]/g, ' ')
  350. // Splits camel cased words
  351. .replace(/([a-z])([A-Z])/g, function(m0, m1, m2) {
  352. return "" + m1 + " " + m2.toLowerCase();
  353. })
  354. .toLowerCase();
  355. },
  356. stringifyValue: function(value) {
  357. return v.prettify(value);
  358. },
  359. isString: function(value) {
  360. return typeof value === 'string';
  361. },
  362. isArray: function(value) {
  363. return {}.toString.call(value) === '[object Array]';
  364. },
  365. // Checks if the object is a hash, which is equivalent to an object that
  366. // is neither an array nor a function.
  367. isHash: function(value) {
  368. return v.isObject(value) && !v.isArray(value) && !v.isFunction(value);
  369. },
  370. contains: function(obj, value) {
  371. if (!v.isDefined(obj)) {
  372. return false;
  373. }
  374. if (v.isArray(obj)) {
  375. return obj.indexOf(value) !== -1;
  376. }
  377. return value in obj;
  378. },
  379. unique: function(array) {
  380. if (!v.isArray(array)) {
  381. return array;
  382. }
  383. return array.filter(function(el, index, array) {
  384. return array.indexOf(el) == index;
  385. });
  386. },
  387. forEachKeyInKeypath: function(object, keypath, callback) {
  388. if (!v.isString(keypath)) {
  389. return undefined;
  390. }
  391. var key = ""
  392. , i
  393. , escape = false;
  394. for (i = 0; i < keypath.length; ++i) {
  395. switch (keypath[i]) {
  396. case '.':
  397. if (escape) {
  398. escape = false;
  399. key += '.';
  400. } else {
  401. object = callback(object, key, false);
  402. key = "";
  403. }
  404. break;
  405. case '\\':
  406. if (escape) {
  407. escape = false;
  408. key += '\\';
  409. } else {
  410. escape = true;
  411. }
  412. break;
  413. default:
  414. escape = false;
  415. key += keypath[i];
  416. break;
  417. }
  418. }
  419. return callback(object, key, true);
  420. },
  421. getDeepObjectValue: function(obj, keypath) {
  422. if (!v.isObject(obj)) {
  423. return undefined;
  424. }
  425. return v.forEachKeyInKeypath(obj, keypath, function(obj, key) {
  426. if (v.isObject(obj)) {
  427. return obj[key];
  428. }
  429. });
  430. },
  431. // This returns an object with all the values of the form.
  432. // It uses the input name as key and the value as value
  433. // So for example this:
  434. // <input type="text" name="email" value="foo@bar.com" />
  435. // would return:
  436. // {email: "foo@bar.com"}
  437. collectFormValues: function(form, options) {
  438. var values = {}
  439. , i
  440. , j
  441. , input
  442. , inputs
  443. , option
  444. , value;
  445. if (v.isJqueryElement(form)) {
  446. form = form[0];
  447. }
  448. if (!form) {
  449. return values;
  450. }
  451. options = options || {};
  452. inputs = form.querySelectorAll("input[name], textarea[name]");
  453. for (i = 0; i < inputs.length; ++i) {
  454. input = inputs.item(i);
  455. if (v.isDefined(input.getAttribute("data-ignored"))) {
  456. continue;
  457. }
  458. value = v.sanitizeFormValue(input.value, options);
  459. if (input.type === "number") {
  460. value = value ? +value : null;
  461. } else if (input.type === "checkbox") {
  462. if (input.attributes.value) {
  463. if (!input.checked) {
  464. value = values[input.name] || null;
  465. }
  466. } else {
  467. value = input.checked;
  468. }
  469. } else if (input.type === "radio") {
  470. if (!input.checked) {
  471. value = values[input.name] || null;
  472. }
  473. }
  474. values[input.name] = value;
  475. }
  476. inputs = form.querySelectorAll("select[name]");
  477. for (i = 0; i < inputs.length; ++i) {
  478. input = inputs.item(i);
  479. if (v.isDefined(input.getAttribute("data-ignored"))) {
  480. continue;
  481. }
  482. if (input.multiple) {
  483. value = [];
  484. for (j in input.options) {
  485. option = input.options[j];
  486. if (option.selected) {
  487. value.push(v.sanitizeFormValue(option.value, options));
  488. }
  489. }
  490. } else {
  491. value = v.sanitizeFormValue(input.options[input.selectedIndex].value, options);
  492. }
  493. values[input.name] = value;
  494. }
  495. return values;
  496. },
  497. sanitizeFormValue: function(value, options) {
  498. if (options.trim && v.isString(value)) {
  499. value = value.trim();
  500. }
  501. if (options.nullify !== false && value === "") {
  502. return null;
  503. }
  504. return value;
  505. },
  506. capitalize: function(str) {
  507. if (!v.isString(str)) {
  508. return str;
  509. }
  510. return str[0].toUpperCase() + str.slice(1);
  511. },
  512. // Remove all errors who's error attribute is empty (null or undefined)
  513. pruneEmptyErrors: function(errors) {
  514. return errors.filter(function(error) {
  515. return !v.isEmpty(error.error);
  516. });
  517. },
  518. // In
  519. // [{error: ["err1", "err2"], ...}]
  520. // Out
  521. // [{error: "err1", ...}, {error: "err2", ...}]
  522. //
  523. // All attributes in an error with multiple messages are duplicated
  524. // when expanding the errors.
  525. expandMultipleErrors: function(errors) {
  526. var ret = [];
  527. errors.forEach(function(error) {
  528. // Removes errors without a message
  529. if (v.isArray(error.error)) {
  530. error.error.forEach(function(msg) {
  531. ret.push(v.extend({}, error, {error: msg}));
  532. });
  533. } else {
  534. ret.push(error);
  535. }
  536. });
  537. return ret;
  538. },
  539. // Converts the error mesages by prepending the attribute name unless the
  540. // message is prefixed by ^
  541. convertErrorMessages: function(errors, options) {
  542. options = options || {};
  543. var ret = [];
  544. errors.forEach(function(errorInfo) {
  545. var error = v.result(errorInfo.error,
  546. errorInfo.value,
  547. errorInfo.attribute,
  548. errorInfo.options,
  549. errorInfo.attributes,
  550. errorInfo.globalOptions);
  551. if (!v.isString(error)) {
  552. ret.push(errorInfo);
  553. return;
  554. }
  555. if (error[0] === '^') {
  556. error = error.slice(1);
  557. } else if (options.fullMessages !== false) {
  558. error = v.capitalize(v.prettify(errorInfo.attribute)) + " " + error;
  559. }
  560. error = error.replace(/\\\^/g, "^");
  561. error = v.format(error, {value: v.stringifyValue(errorInfo.value)});
  562. ret.push(v.extend({}, errorInfo, {error: error}));
  563. });
  564. return ret;
  565. },
  566. // In:
  567. // [{attribute: "<attributeName>", ...}]
  568. // Out:
  569. // {"<attributeName>": [{attribute: "<attributeName>", ...}]}
  570. groupErrorsByAttribute: function(errors) {
  571. var ret = {};
  572. errors.forEach(function(error) {
  573. var list = ret[error.attribute];
  574. if (list) {
  575. list.push(error);
  576. } else {
  577. ret[error.attribute] = [error];
  578. }
  579. });
  580. return ret;
  581. },
  582. // In:
  583. // [{error: "<message 1>", ...}, {error: "<message 2>", ...}]
  584. // Out:
  585. // ["<message 1>", "<message 2>"]
  586. flattenErrorsToArray: function(errors) {
  587. return errors
  588. .map(function(error) { return error.error; })
  589. .filter(function(value, index, self) {
  590. return self.indexOf(value) === index;
  591. });
  592. },
  593. cleanAttributes: function(attributes, whitelist) {
  594. function whitelistCreator(obj, key, last) {
  595. if (v.isObject(obj[key])) {
  596. return obj[key];
  597. }
  598. return (obj[key] = last ? true : {});
  599. }
  600. function buildObjectWhitelist(whitelist) {
  601. var ow = {}
  602. , lastObject
  603. , attr;
  604. for (attr in whitelist) {
  605. if (!whitelist[attr]) {
  606. continue;
  607. }
  608. v.forEachKeyInKeypath(ow, attr, whitelistCreator);
  609. }
  610. return ow;
  611. }
  612. function cleanRecursive(attributes, whitelist) {
  613. if (!v.isObject(attributes)) {
  614. return attributes;
  615. }
  616. var ret = v.extend({}, attributes)
  617. , w
  618. , attribute;
  619. for (attribute in attributes) {
  620. w = whitelist[attribute];
  621. if (v.isObject(w)) {
  622. ret[attribute] = cleanRecursive(ret[attribute], w);
  623. } else if (!w) {
  624. delete ret[attribute];
  625. }
  626. }
  627. return ret;
  628. }
  629. if (!v.isObject(whitelist) || !v.isObject(attributes)) {
  630. return {};
  631. }
  632. whitelist = buildObjectWhitelist(whitelist);
  633. return cleanRecursive(attributes, whitelist);
  634. },
  635. exposeModule: function(validate, root, exports, module, define) {
  636. if (exports) {
  637. if (module && module.exports) {
  638. exports = module.exports = validate;
  639. }
  640. exports.validate = validate;
  641. } else {
  642. root.validate = validate;
  643. if (validate.isFunction(define) && define.amd) {
  644. define([], function () { return validate; });
  645. }
  646. }
  647. },
  648. warn: function(msg) {
  649. if (typeof console !== "undefined" && console.warn) {
  650. console.warn("[validate.js] " + msg);
  651. }
  652. },
  653. error: function(msg) {
  654. if (typeof console !== "undefined" && console.error) {
  655. console.error("[validate.js] " + msg);
  656. }
  657. }
  658. });
  659. validate.validators = {
  660. // Presence validates that the value isn't empty
  661. presence: function(value, options) {
  662. options = v.extend({}, this.options, options);
  663. if (options.allowEmpty ? !v.isDefined(value) : v.isEmpty(value)) {
  664. return options.message || this.message || "can't be blank";
  665. }
  666. },
  667. length: function(value, options, attribute) {
  668. // Empty values are allowed
  669. if (!v.isDefined(value)) {
  670. return;
  671. }
  672. options = v.extend({}, this.options, options);
  673. var is = options.is
  674. , maximum = options.maximum
  675. , minimum = options.minimum
  676. , tokenizer = options.tokenizer || function(val) { return val; }
  677. , err
  678. , errors = [];
  679. value = tokenizer(value);
  680. var length = value.length;
  681. if(!v.isNumber(length)) {
  682. v.error(v.format("Attribute %{attr} has a non numeric value for `length`", {attr: attribute}));
  683. return options.message || this.notValid || "has an incorrect length";
  684. }
  685. // Is checks
  686. if (v.isNumber(is) && length !== is) {
  687. err = options.wrongLength ||
  688. this.wrongLength ||
  689. "is the wrong length (should be %{count} characters)";
  690. errors.push(v.format(err, {count: is}));
  691. }
  692. if (v.isNumber(minimum) && length < minimum) {
  693. err = options.tooShort ||
  694. this.tooShort ||
  695. "is too short (minimum is %{count} characters)";
  696. errors.push(v.format(err, {count: minimum}));
  697. }
  698. if (v.isNumber(maximum) && length > maximum) {
  699. err = options.tooLong ||
  700. this.tooLong ||
  701. "is too long (maximum is %{count} characters)";
  702. errors.push(v.format(err, {count: maximum}));
  703. }
  704. if (errors.length > 0) {
  705. return options.message || errors;
  706. }
  707. },
  708. numericality: function(value, options) {
  709. // Empty values are fine
  710. if (!v.isDefined(value)) {
  711. return;
  712. }
  713. options = v.extend({}, this.options, options);
  714. var errors = []
  715. , name
  716. , count
  717. , checks = {
  718. greaterThan: function(v, c) { return v > c; },
  719. greaterThanOrEqualTo: function(v, c) { return v >= c; },
  720. equalTo: function(v, c) { return v === c; },
  721. lessThan: function(v, c) { return v < c; },
  722. lessThanOrEqualTo: function(v, c) { return v <= c; },
  723. divisibleBy: function(v, c) { return v % c === 0; }
  724. };
  725. // Strict will check that it is a valid looking number
  726. if (v.isString(value) && options.strict) {
  727. var pattern = "^(0|[1-9]\\d*)";
  728. if (!options.onlyInteger) {
  729. pattern += "(\\.\\d+)?";
  730. }
  731. pattern += "$";
  732. if (!(new RegExp(pattern).test(value))) {
  733. return options.message ||
  734. options.notValid ||
  735. this.notValid ||
  736. this.message ||
  737. "must be a valid number";
  738. }
  739. }
  740. // Coerce the value to a number unless we're being strict.
  741. if (options.noStrings !== true && v.isString(value) && !v.isEmpty(value)) {
  742. value = +value;
  743. }
  744. // If it's not a number we shouldn't continue since it will compare it.
  745. if (!v.isNumber(value)) {
  746. return options.message ||
  747. options.notValid ||
  748. this.notValid ||
  749. this.message ||
  750. "is not a number";
  751. }
  752. // Same logic as above, sort of. Don't bother with comparisons if this
  753. // doesn't pass.
  754. if (options.onlyInteger && !v.isInteger(value)) {
  755. return options.message ||
  756. options.notInteger ||
  757. this.notInteger ||
  758. this.message ||
  759. "must be an integer";
  760. }
  761. for (name in checks) {
  762. count = options[name];
  763. if (v.isNumber(count) && !checks[name](value, count)) {
  764. // This picks the default message if specified
  765. // For example the greaterThan check uses the message from
  766. // this.notGreaterThan so we capitalize the name and prepend "not"
  767. var key = "not" + v.capitalize(name);
  768. var msg = options[key] ||
  769. this[key] ||
  770. this.message ||
  771. "must be %{type} %{count}";
  772. errors.push(v.format(msg, {
  773. count: count,
  774. type: v.prettify(name)
  775. }));
  776. }
  777. }
  778. if (options.odd && value % 2 !== 1) {
  779. errors.push(options.notOdd ||
  780. this.notOdd ||
  781. this.message ||
  782. "must be odd");
  783. }
  784. if (options.even && value % 2 !== 0) {
  785. errors.push(options.notEven ||
  786. this.notEven ||
  787. this.message ||
  788. "must be even");
  789. }
  790. if (errors.length) {
  791. return options.message || errors;
  792. }
  793. },
  794. datetime: v.extend(function(value, options) {
  795. if (!v.isFunction(this.parse) || !v.isFunction(this.format)) {
  796. throw new Error("Both the parse and format functions needs to be set to use the datetime/date validator");
  797. }
  798. // Empty values are fine
  799. if (!v.isDefined(value)) {
  800. return;
  801. }
  802. options = v.extend({}, this.options, options);
  803. var err
  804. , errors = []
  805. , earliest = options.earliest ? this.parse(options.earliest, options) : NaN
  806. , latest = options.latest ? this.parse(options.latest, options) : NaN;
  807. value = this.parse(value, options);
  808. // 86400000 is the number of seconds in a day, this is used to remove
  809. // the time from the date
  810. if (isNaN(value) || options.dateOnly && value % 86400000 !== 0) {
  811. err = options.notValid ||
  812. options.message ||
  813. this.notValid ||
  814. "must be a valid date";
  815. return v.format(err, {value: arguments[0]});
  816. }
  817. if (!isNaN(earliest) && value < earliest) {
  818. err = options.tooEarly ||
  819. options.message ||
  820. this.tooEarly ||
  821. "must be no earlier than %{date}";
  822. err = v.format(err, {
  823. value: this.format(value, options),
  824. date: this.format(earliest, options)
  825. });
  826. errors.push(err);
  827. }
  828. if (!isNaN(latest) && value > latest) {
  829. err = options.tooLate ||
  830. options.message ||
  831. this.tooLate ||
  832. "must be no later than %{date}";
  833. err = v.format(err, {
  834. date: this.format(latest, options),
  835. value: this.format(value, options)
  836. });
  837. errors.push(err);
  838. }
  839. if (errors.length) {
  840. return v.unique(errors);
  841. }
  842. }, {
  843. parse: null,
  844. format: null
  845. }),
  846. date: function(value, options) {
  847. options = v.extend({}, options, {dateOnly: true});
  848. return v.validators.datetime.call(v.validators.datetime, value, options);
  849. },
  850. format: function(value, options) {
  851. if (v.isString(options) || (options instanceof RegExp)) {
  852. options = {pattern: options};
  853. }
  854. options = v.extend({}, this.options, options);
  855. var message = options.message || this.message || "is invalid"
  856. , pattern = options.pattern
  857. , match;
  858. // Empty values are allowed
  859. if (!v.isDefined(value)) {
  860. return;
  861. }
  862. if (!v.isString(value)) {
  863. return message;
  864. }
  865. if (v.isString(pattern)) {
  866. pattern = new RegExp(options.pattern, options.flags);
  867. }
  868. match = pattern.exec(value);
  869. if (!match || match[0].length != value.length) {
  870. return message;
  871. }
  872. },
  873. inclusion: function(value, options) {
  874. // Empty values are fine
  875. if (!v.isDefined(value)) {
  876. return;
  877. }
  878. if (v.isArray(options)) {
  879. options = {within: options};
  880. }
  881. options = v.extend({}, this.options, options);
  882. if (v.contains(options.within, value)) {
  883. return;
  884. }
  885. var message = options.message ||
  886. this.message ||
  887. "^%{value} is not included in the list";
  888. return v.format(message, {value: value});
  889. },
  890. exclusion: function(value, options) {
  891. // Empty values are fine
  892. if (!v.isDefined(value)) {
  893. return;
  894. }
  895. if (v.isArray(options)) {
  896. options = {within: options};
  897. }
  898. options = v.extend({}, this.options, options);
  899. if (!v.contains(options.within, value)) {
  900. return;
  901. }
  902. var message = options.message || this.message || "^%{value} is restricted";
  903. return v.format(message, {value: value});
  904. },
  905. email: v.extend(function(value, options) {
  906. options = v.extend({}, this.options, options);
  907. var message = options.message || this.message || "is not a valid email";
  908. // Empty values are fine
  909. if (!v.isDefined(value)) {
  910. return;
  911. }
  912. if (!v.isString(value)) {
  913. return message;
  914. }
  915. if (!this.PATTERN.exec(value)) {
  916. return message;
  917. }
  918. }, {
  919. PATTERN: /^[a-z0-9\u007F-\uffff!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9\u007F-\uffff!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/i
  920. }),
  921. equality: function(value, options, attribute, attributes) {
  922. if (!v.isDefined(value)) {
  923. return;
  924. }
  925. if (v.isString(options)) {
  926. options = {attribute: options};
  927. }
  928. options = v.extend({}, this.options, options);
  929. var message = options.message ||
  930. this.message ||
  931. "is not equal to %{attribute}";
  932. if (v.isEmpty(options.attribute) || !v.isString(options.attribute)) {
  933. throw new Error("The attribute must be a non empty string");
  934. }
  935. var otherValue = v.getDeepObjectValue(attributes, options.attribute)
  936. , comparator = options.comparator || function(v1, v2) {
  937. return v1 === v2;
  938. };
  939. if (!comparator(value, otherValue, options, attribute, attributes)) {
  940. return v.format(message, {attribute: v.prettify(options.attribute)});
  941. }
  942. },
  943. // A URL validator that is used to validate URLs with the ability to
  944. // restrict schemes and some domains.
  945. url: function(value, options) {
  946. if (!v.isDefined(value)) {
  947. return;
  948. }
  949. options = v.extend({}, this.options, options);
  950. var message = options.message || this.message || "is not a valid url"
  951. , schemes = options.schemes || this.schemes || ['http', 'https']
  952. , allowLocal = options.allowLocal || this.allowLocal || false;
  953. if (!v.isString(value)) {
  954. return message;
  955. }
  956. // https://gist.github.com/dperini/729294
  957. var regex =
  958. "^" +
  959. // protocol identifier
  960. "(?:(?:" + schemes.join("|") + ")://)" +
  961. // user:pass authentication
  962. "(?:\\S+(?::\\S*)?@)?" +
  963. "(?:";
  964. var tld = "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))";
  965. if (allowLocal) {
  966. tld += "?";
  967. } else {
  968. regex +=
  969. // IP address exclusion
  970. // private & local networks
  971. "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
  972. "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
  973. "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})";
  974. }
  975. regex +=
  976. // IP address dotted notation octets
  977. // excludes loopback network 0.0.0.0
  978. // excludes reserved space >= 224.0.0.0
  979. // excludes network & broacast addresses
  980. // (first & last IP address of each class)
  981. "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
  982. "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
  983. "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
  984. "|" +
  985. // host name
  986. "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
  987. // domain name
  988. "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
  989. tld +
  990. ")" +
  991. // port number
  992. "(?::\\d{2,5})?" +
  993. // resource path
  994. "(?:[/?#]\\S*)?" +
  995. "$";
  996. var PATTERN = new RegExp(regex, 'i');
  997. if (!PATTERN.exec(value)) {
  998. return message;
  999. }
  1000. }
  1001. };
  1002. validate.formatters = {
  1003. detailed: function(errors) {return errors;},
  1004. flat: v.flattenErrorsToArray,
  1005. grouped: function(errors) {
  1006. var attr;
  1007. errors = v.groupErrorsByAttribute(errors);
  1008. for (attr in errors) {
  1009. errors[attr] = v.flattenErrorsToArray(errors[attr]);
  1010. }
  1011. return errors;
  1012. },
  1013. constraint: function(errors) {
  1014. var attr;
  1015. errors = v.groupErrorsByAttribute(errors);
  1016. for (attr in errors) {
  1017. errors[attr] = errors[attr].map(function(result) {
  1018. return result.validator;
  1019. }).sort();
  1020. }
  1021. return errors;
  1022. }
  1023. };
  1024. validate.exposeModule(validate, this, exports, module, define);
  1025. }).call(this,
  1026. typeof exports !== 'undefined' ? /* istanbul ignore next */ exports : null,
  1027. typeof module !== 'undefined' ? /* istanbul ignore next */ module : null,
  1028. typeof define !== 'undefined' ? /* istanbul ignore next */ define : null);