vanadium.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. /*
  2. @author Daniel Kwiecinski <daniel.kwiecinski@lambder.com>
  3. @copyright 2009 Daniel Kwiecinski.
  4. 本插件原作者Vanadium,原文请移步前往http://vanadiumjs.com/查看
  5. 本插件由Mr.Think中文整理,Mr.Think的博客:http://MrThink.net/
  6. 转载及使用请务必注明原作者.
  7. */
  8. //********** vanadium-jquery.js **********
  9. Vanadium = {};
  10. Vanadium.Version = '0.1';
  11. Vanadium.CompatibleWithJQuery = '1.3.2';
  12. Vanadium.Type = "jquery";
  13. if (jQuery().jquery.indexOf(Vanadium.CompatibleWithJQuery) != 0 && window.console && window.console.warn)
  14. console.warn("This version of Vanadium is tested with jQuery " + Vanadium.CompatibleWithJQuery +
  15. " it may not work as expected with this version (" + jQuery().jquery + ")");
  16. Vanadium.each = jQuery.each;
  17. Vanadium.all_elements = function() {
  18. return jQuery('*');
  19. };
  20. Vanadium.partition = function(elements, dyscriminator) {
  21. var left = [];
  22. var right = [];
  23. Vanadium.each(elements, function() {
  24. if (dyscriminator(this)) {
  25. left.push(this);
  26. } else {
  27. right.push(this);
  28. }
  29. ;
  30. });
  31. return [left, right];
  32. };
  33. //********** vanadium-hashmap.js **********
  34. var HashMap = function() {
  35. this.initialize();
  36. }
  37. HashMap.hashmap_instance_id = 0;
  38. HashMap.prototype = {
  39. hashkey_prefix: "<#HashMapHashkeyPerfix>",
  40. hashcode_field: "<#HashMapHashcodeField>",
  41. initialize: function() {
  42. this.backing_hash = {};
  43. this.code = 0;
  44. HashMap.hashmap_instance_id += 1;
  45. this.instance_id = HashMap.hashmap_instance_id;
  46. },
  47. hashcodeField: function() {
  48. return this.hashcode_field + this.instance_id;
  49. },
  50. /*
  51. maps value to key returning previous assocciation
  52. */
  53. put: function(key, value) {
  54. var prev;
  55. if (key && value) {
  56. var hashCode;
  57. if (typeof(key) === "number" || typeof(key) === "string") {
  58. hashCode = key;
  59. } else {
  60. hashCode = key[this.hashcodeField()];
  61. }
  62. if (hashCode) {
  63. prev = this.backing_hash[hashCode];
  64. } else {
  65. this.code += 1;
  66. hashCode = this.hashkey_prefix + this.code;
  67. key[this.hashcodeField()] = hashCode;
  68. }
  69. this.backing_hash[hashCode] = [key, value];
  70. }
  71. return prev === undefined ? undefined : prev[1];
  72. },
  73. /*
  74. returns value associated with given key
  75. */
  76. get: function(key) {
  77. var value;
  78. if (key) {
  79. var hashCode;
  80. if (typeof(key) === "number" || typeof(key) === "string") {
  81. hashCode = key;
  82. } else {
  83. hashCode = key[this.hashcodeField()];
  84. }
  85. if (hashCode) {
  86. value = this.backing_hash[hashCode];
  87. }
  88. }
  89. return value === undefined ? undefined : value[1];
  90. },
  91. /*
  92. deletes association by given key.
  93. Returns true if the assocciation existed, false otherwise
  94. */
  95. del: function(key) {
  96. var success = false;
  97. if (key) {
  98. var hashCode;
  99. if (typeof(key) === "number" || typeof(key) === "string") {
  100. hashCode = key;
  101. } else {
  102. hashCode = key[this.hashcodeField()];
  103. }
  104. if (hashCode) {
  105. var prev = this.backing_hash[hashCode];
  106. this.backing_hash[hashCode] = undefined;
  107. if (prev !== undefined){
  108. key[this.hashcodeField()] = undefined; //let's clean the key object
  109. success = true;
  110. }
  111. }
  112. }
  113. return success;
  114. },
  115. /*
  116. iterate over key-value pairs passing them to provided callback
  117. the iteration process is interrupted when the callback returns false.
  118. the execution context of the callback is the value of the key-value pair
  119. @ returns the HashMap (so we can chain) (
  120. */
  121. each: function(callback, args) {
  122. var key;
  123. for (key in this.backing_hash){
  124. if (callback.call(this.backing_hash[key][1], this.backing_hash[key][0], this.backing_hash[key][1]) === false)
  125. break;
  126. }
  127. return this;
  128. },
  129. toString: function() {
  130. return "HashMapJS"
  131. }
  132. }
  133. //********** vanadium-container.js **********
  134. Vanadium.containers = new HashMap();
  135. var ContainerValidation = function(html_element) {
  136. this.initialize(html_element)
  137. }
  138. ContainerValidation.prototype = {
  139. initialize: function(html_element) {
  140. this.html_element = html_element;
  141. this.elements = [];
  142. },
  143. add_element: function(element) {
  144. this.elements.push(element);
  145. },
  146. decorate: function() {
  147. var valid = null;
  148. for (var id in this.elements) {
  149. if (this.elements[id].invalid === undefined) {
  150. valid = undefined;
  151. } else if (this.elements[id].invalid === true) {
  152. valid = false;
  153. break;
  154. } else if (this.elements[id].invalid === false && valid === null) {
  155. valid = true;
  156. }
  157. }
  158. if (valid === undefined) {
  159. jQuery(this.html_element).removeClass(Vanadium.config.invalid_class);
  160. jQuery(this.html_element).removeClass(Vanadium.config.valid_class);
  161. } else if (valid) {
  162. jQuery(this.html_element).removeClass(Vanadium.config.invalid_class);
  163. jQuery(this.html_element).addClass(Vanadium.config.valid_class);
  164. } else {
  165. jQuery(this.html_element).removeClass(Vanadium.config.valid_class);
  166. jQuery(this.html_element).addClass(Vanadium.config.invalid_class);
  167. }
  168. }
  169. }
  170. //********** vanadium-form.js **********
  171. var VanadiumForm = function(element) {
  172. this.initialize(element);
  173. }
  174. Vanadium.forms = new HashMap();
  175. VanadiumForm.add_element = function(validation_element) {
  176. var form = validation_element.element.form;
  177. if (form) {
  178. var vanadum_form = Vanadium.forms.get(form);
  179. if (vanadum_form) {
  180. vanadum_form.validation_elements.push(validation_element);
  181. } else {
  182. vanadum_form = new VanadiumForm(validation_element);
  183. Vanadium.forms.put(form, vanadum_form);
  184. }
  185. }
  186. }
  187. VanadiumForm.prototype = {
  188. initialize: function(validation_element) {
  189. this.validation_elements = [validation_element];
  190. this.form = validation_element.element.form;
  191. var self = this;
  192. var on_form_submit = function() {
  193. var validation_result = self.validate();
  194. var success = true;
  195. validation_result.each(function(_element, validation_results) {
  196. for (var r in validation_results) {
  197. if (validation_results[r].success == false) {
  198. success = false;
  199. break;
  200. }
  201. }
  202. if (success == false) {
  203. return false;// break from hashmap iteration
  204. }
  205. });
  206. if (!success) {
  207. self.decorate();
  208. return false;
  209. }
  210. return success;
  211. };
  212. //jQuery(this.form).submit(on_form_submit);
  213. jQuery(this.form).find(':submit').click(function() {
  214. return on_form_submit();
  215. });
  216. this.form.decorate = function() {
  217. self.decorate();
  218. }
  219. },
  220. validate: function() {
  221. var validation = new HashMap();
  222. Vanadium.each(this.validation_elements,
  223. function() {
  224. validation.put(this, this.validate());
  225. });
  226. return validation;
  227. },
  228. decorate: function(validation_results) {
  229. if (arguments.length == 0) {
  230. validation_results = this.validate();
  231. }
  232. validation_results.each(function(element, element_validation_results) {
  233. element.decorate(element_validation_results);
  234. });
  235. },
  236. validateAndDecorate: function() {
  237. this.decorate(this.validate())
  238. }
  239. }
  240. //********** vanadium-base.js **********
  241. Vanadium.validators_types = {};
  242. Vanadium.elements_validators_by_id = {};
  243. Vanadium.all_elements_validators = [];
  244. Vanadium.created_advices = [];
  245. Vanadium.all_html_elements = new HashMap();
  246. Vanadium.empty_advice_marker_class = '-vanadium-empty-advice-'
  247. //validation rules
  248. Vanadium.rules = {}
  249. Vanadium.init = function() {
  250. this.setupValidatorTypes();
  251. this.scan_dom();
  252. }
  253. Vanadium.addValidatorType = function(className, validationFunction, error_message, message, init) {
  254. this.validators_types[className] = new Vanadium.Type(className, validationFunction, error_message, message, init);
  255. };
  256. Vanadium.addValidatorTypes = function(validators_args) {
  257. var self = this;
  258. Vanadium.each(validators_args,
  259. function() {
  260. Vanadium.addValidatorType.apply(self, this);
  261. }
  262. )
  263. };
  264. Vanadium.scan_dom = function() {
  265. Vanadium.each(Vanadium.all_elements(),
  266. function(_idx, child) {
  267. var class_names = child.className.split(' ');
  268. if (Vanadium.is_input_element(child)) {
  269. var element_validation = new ElementValidation(child);
  270. if (child.id)
  271. Vanadium.elements_validators_by_id[child.id] = element_validation
  272. Vanadium.all_elements_validators.push(element_validation)
  273. Vanadium.all_html_elements.put(child, element_validation);
  274. VanadiumForm.add_element(element_validation);
  275. //create validation rules based on class markup
  276. Vanadium.each(class_names,
  277. function() {
  278. var parameters = Vanadium.parse_class_name(this);
  279. /*'this' is class_name*/
  280. if (parameters) {
  281. Vanadium.add_validation_instance(element_validation, parameters);
  282. Vanadium.add_validation_modifier(element_validation, parameters);
  283. }
  284. });
  285. //create validation rules based on json providen in VanadiumRules variable
  286. Vanadium.each(Vanadium.get_rules(child.id),
  287. function() {
  288. var parameters = this;
  289. if (parameters) {
  290. Vanadium.add_validation_instance(element_validation, parameters);
  291. Vanadium.add_validation_modifier(element_validation, parameters);
  292. }
  293. });
  294. element_validation.setup();
  295. } else {
  296. Vanadium.add_validation_container(child);
  297. }
  298. })
  299. }
  300. Vanadium.add_validation_container = function(element) {
  301. var class_names = element.className.split(' ');
  302. Vanadium.each(class_names,
  303. function() {
  304. var parameters = Vanadium.parse_class_name(this);
  305. if (parameters[0] == 'container') {
  306. Vanadium.containers.put(element, new ContainerValidation(element));
  307. return true
  308. }
  309. });
  310. Vanadium.each(Vanadium.get_rules(element.id),
  311. function() {
  312. var rule = this;
  313. if (rule == 'container') {
  314. Vanadium.containers.put(element, new ContainerValidation(element));
  315. return true
  316. }
  317. });
  318. }
  319. Vanadium.get_rules = function(element_id) {
  320. var rule_from_string_or_hash = function(r) {
  321. if (typeof r === "string") {
  322. return [r];
  323. } else if (Vanadium.isArray(r)) {
  324. return r;
  325. } else if (typeof(r) === "object") {
  326. return [r.validator, r.parameter, r.advice];
  327. } else {
  328. return undefined;
  329. }
  330. }
  331. //
  332. var rules = [];
  333. //
  334. var rs = Vanadium.rules[element_id];
  335. if (typeof rs === "undefined") {
  336. return [];
  337. } else if (typeof rs === "string") {
  338. rules.push(rs);
  339. } else if (Vanadium.isArray(rs)) {
  340. for (var r in rs) {
  341. rules.push(rule_from_string_or_hash(rs[r]));
  342. }
  343. } else if (typeof(rs) === "object") {
  344. rules.push(rule_from_string_or_hash(rs));
  345. }
  346. return rules;
  347. }
  348. Vanadium.parse_class_name = function(class_name) {
  349. if (class_name.indexOf(Vanadium.config.prefix) == 0) {
  350. var v_params = class_name.substr(Vanadium.config.prefix.length).split(Vanadium.config.separator)
  351. for (var key in v_params) {
  352. if (v_params[key] == "") {
  353. v_params[key] = undefined
  354. }
  355. }
  356. return v_params;
  357. } else {
  358. return [];
  359. }
  360. }
  361. Vanadium.add_validation_instance = function(element_validation, parameters) {
  362. var v_name = parameters[0];
  363. var v_param = parameters[1];
  364. var v_advice_id = parameters[2];
  365. var validator_type = Vanadium.validators_types[v_name]
  366. if (validator_type) {
  367. element_validation.add_validation_instance(validator_type, v_param, v_advice_id);
  368. }
  369. }
  370. Vanadium.add_validation_modifier = function(element_validation, parameters) {
  371. var m_name = parameters[0];
  372. var m_param = parameters[1];
  373. if (m_name == 'only_on_blur' || m_name == 'only_on_submit' || m_name == 'wait' || m_name == 'advice') {
  374. element_validation.add_validation_modifier(m_name, m_param);
  375. }
  376. }
  377. Vanadium.validate = function() {
  378. var validation = new HashMap();
  379. Vanadium.each(Vanadium.all_elements_validators,
  380. function() {
  381. validation.put(this, this.validate());
  382. });
  383. return validation;
  384. }
  385. Vanadium.validateAndDecorate = function(html_element) {
  386. if (typeof html_element === "undefined") { // validate and decorate everything
  387. Vanadium.decorate(Vanadium.validate());
  388. } else if (html_element.nodeType == 1) {
  389. var element_validation = Vanadium.all_html_elements.get(html_element) || Vanadium.forms.get(html_element);
  390. if (element_validation) {
  391. element_validation.validateAndDecorate(false);
  392. }
  393. }
  394. }
  395. Vanadium.decorate = function(validation_results) {
  396. if (typeof validation_results === "object") {
  397. if (validation_results.toString() == "HashMapJS") {
  398. validation_results.each(function(element, element_validation_results) {
  399. element.decorate(element_validation_results);
  400. })
  401. } else {//this is probably json structure representing validation result
  402. var element_id;
  403. for (element_id in validation_results) {
  404. var element = Vanadium.elements_validators_by_id[element_id];
  405. if (element) {
  406. element.decorate(validation_results[element_id]);
  407. }
  408. }
  409. }
  410. }
  411. }
  412. Vanadium.reset = function() {
  413. Vanadium.each(Vanadium.all_elements_validators,
  414. function() {
  415. this.reset();
  416. });
  417. }
  418. //********** vanadium-utils.js **********
  419. Vanadium.isArray = function(object) {
  420. return object != null && typeof object == "object" &&
  421. 'splice' in object && 'join' in object;
  422. }
  423. Vanadium.isFunction = function(object) {
  424. return object != null && object.toString() === "[object Function]";
  425. }
  426. Vanadium.extend = function(extension) {
  427. var args = [Vanadium];
  428. for (var idx = 0; idx < arguments.length; idx++) {
  429. args.push(arguments[idx]);
  430. }
  431. return jQuery.extend.apply(jQuery, args);
  432. }
  433. Vanadium.bind = function(fun, context) {
  434. return function() {
  435. return fun.apply(context, arguments);
  436. }
  437. }
  438. //********** vanadium-dom.js **********
  439. Vanadium.extend(
  440. {
  441. /**
  442. * gets the type of element, to check whether it is compatible
  443. */
  444. getElementType: function(element) {
  445. switch (true) {
  446. case (element.nodeName.toUpperCase() == 'TEXTAREA'):
  447. return Vanadium.TEXTAREA;
  448. case (element.nodeName.toUpperCase() == 'INPUT' && element.type.toUpperCase() == 'TEXT'):
  449. return Vanadium.TEXT;
  450. case (element.nodeName.toUpperCase() == 'INPUT' && element.type.toUpperCase() == 'PASSWORD'):
  451. return Vanadium.PASSWORD;
  452. case (element.nodeName.toUpperCase() == 'INPUT' && element.type.toUpperCase() == 'CHECKBOX'):
  453. return Vanadium.CHECKBOX;
  454. case (element.nodeName.toUpperCase() == 'INPUT' && element.type.toUpperCase() == 'FILE'):
  455. return Vanadium.FILE;
  456. case (element.nodeName.toUpperCase() == 'SELECT'):
  457. return Vanadium.SELECT;
  458. case (element.nodeName.toUpperCase() == 'INPUT'):
  459. throw new Error('Vanadium::getElementType - Cannot use Vanadium on an ' + element.type + ' input!');
  460. default:
  461. throw new Error('Vanadium::getElementType - Element must be an input, select, or textarea!');
  462. }
  463. ;
  464. },
  465. is_input_element : function(element) {
  466. return (element.nodeName.toUpperCase() == 'TEXTAREA') ||
  467. (element.nodeName.toUpperCase() == 'INPUT' && element.type.toUpperCase() == 'TEXT') ||
  468. (element.nodeName.toUpperCase() == 'INPUT' && element.type.toUpperCase() == 'PASSWORD') ||
  469. (element.nodeName.toUpperCase() == 'INPUT' && element.type.toUpperCase() == 'CHECKBOX') ||
  470. (element.nodeName.toUpperCase() == 'INPUT' && element.type.toUpperCase() == 'FILE') ||
  471. (element.nodeName.toUpperCase() == 'SELECT')
  472. },
  473. /**
  474. * makes a span containg the passed or failed advice
  475. *
  476. * @return {HTMLSpanObject} - a span element with the advice message in it
  477. */
  478. createAdvice: function(element, advice_id, message) {
  479. var advice = document.createElement('span');
  480. advice.id = advice_id;
  481. var textNode = document.createTextNode(message);
  482. advice.appendChild(textNode);
  483. element.parentNode.insertBefore(advice, element.nextSibling);
  484. this.created_advices.push(advice);
  485. },
  486. /**
  487. * adds the class of the element/advice/container to indicte if valid or not
  488. */
  489. addValidationClass: function(element, valid) {
  490. if (element) {
  491. this.removeValidationClass(element);
  492. if (valid) {
  493. element.className += ' ' + Vanadium.config.valid_class;
  494. } else {
  495. element.className += ' ' + Vanadium.config.invalid_class;
  496. }
  497. ;
  498. }
  499. ;
  500. },
  501. /**
  502. * removes the class that has been applied to the element/advice/container to indicte if valid or not
  503. */
  504. removeValidationClass: function(element) {
  505. if (element) {
  506. if (element.className.indexOf(Vanadium.config.invalid_class) != -1) element.className = element.className.split(Vanadium.config.invalid_class).join(' ');
  507. if (element.className.indexOf(Vanadium.config.valid_class) != -1) element.className = element.className.split(Vanadium.config.valid_class).join(' ');
  508. }
  509. ;
  510. },
  511. /** element types constants ****/
  512. TEXTAREA: 1,
  513. TEXT: 2,
  514. PASSWORD: 3,
  515. CHECKBOX: 4,
  516. SELECT: 5,
  517. FILE: 6
  518. }
  519. );
  520. //-------------------- vanadium-element.js -----------------------------
  521. ElementValidation = function(element) {
  522. this.initialize(element)
  523. };
  524. ElementValidation.prototype = {
  525. initialize: function(element) {
  526. this.virgin = true;
  527. this.element = element;
  528. this.validations = [];
  529. this.only_on_blur = false;
  530. this.only_on_submit = false;
  531. this.wait = 100;
  532. this.created_advices = [];
  533. this.decorated = false;
  534. this.containers = null;
  535. this.invalid = undefined;
  536. this.advice_id = undefined; //this is general common advice for all validation instances having no specific advice defined
  537. },
  538. add_validation_instance: function(validator_type, param, advice_id) {
  539. this.validations.push(new Validation(this.element, validator_type, param, advice_id));
  540. },
  541. add_validation_modifier: function(modifier, param) {
  542. if (modifier == 'only_on_blur') {
  543. // whether you want it to validate as you type or only on blur (DEFAULT: false)
  544. this.only_on_blur = true
  545. } else if (modifier == 'only_on_submit') {
  546. // whether should be validated only when the form it belongs to is submitted (DEFAULT: false)
  547. this.only_on_submit = true
  548. } else if (modifier == 'wait') {
  549. // the time you want it to pause from the last keystroke before it validates (ms) (DEFAULT: 0)
  550. var milisesonds = parseInt(param);
  551. if (milisesonds != NaN && typeof(milisesonds) === "number") {
  552. this.wait = milisesonds;
  553. }
  554. ;
  555. } else if (modifier == 'advice') {
  556. var advice = document.getElementById(param);
  557. if (advice) {
  558. this.advice_id = param;
  559. }
  560. }
  561. ;
  562. },
  563. element_containers: function() {
  564. if (this.containers === null) {
  565. this.containers = new HashMap();
  566. var parent = this.element.parentNode;
  567. //search up the DOM tree
  568. while (parent != document) {
  569. var container = Vanadium.containers.get(parent);
  570. if (container) {
  571. container.add_element(this);
  572. this.containers.put(parent, container);
  573. }
  574. ;
  575. parent = parent.parentNode;
  576. }
  577. ;
  578. }
  579. ;
  580. return this.containers;
  581. },
  582. // context - the contect in which decoration_callback should be invoked
  583. // decoration_callback - the decoration used by asynchronous validation
  584. validate: function(decoration_context, decoration_callback) {
  585. var result = [];
  586. Vanadium.each(this.validations, function() {
  587. result.push(this.validate(decoration_context, decoration_callback));
  588. });
  589. return result;
  590. },
  591. decorate: function(element_validation_results, do_not_reset) {
  592. if (!do_not_reset) {
  593. this.reset();
  594. }
  595. this.decorated = true;
  596. var self = this;
  597. var passed_and_failed = Vanadium.partition(element_validation_results, function(validation_result) {
  598. return validation_result.success
  599. });
  600. var passed = passed_and_failed[0];
  601. var failed = passed_and_failed[1];
  602. // add apropirate CSS class to the validated element
  603. if (failed.length > 0) {
  604. this.invalid = true; //mark this validation element as invalid
  605. Vanadium.addValidationClass(this.element, false);
  606. } else if (passed.length > 0 && !this.invalid) { //when valid result comes but the previous was invalid and no reset was done, the invalid flag should stay unchanged
  607. this.invalid = false; //mark this validation element as valid
  608. Vanadium.addValidationClass(this.element, true);
  609. } else {
  610. this.invalid = undefined; //mark this validation element as undefined
  611. }
  612. ;
  613. // add apropirate CSS class to the validated element's containers
  614. this.element_containers().each(function(_element, container) {
  615. container.decorate();
  616. });
  617. //
  618. Vanadium.each(failed, function(_idx, validation_result) {
  619. var advice = undefined;
  620. if (self.advice_id) {
  621. advice = document.getElementById(self.advice_id);
  622. }
  623. if (advice || validation_result.advice_id) {
  624. advice = advice || document.getElementById(validation_result.advice_id);
  625. if (advice) {
  626. jQuery(advice).addClass(Vanadium.config.advice_class);
  627. var advice_is_empty = advice.childNodes.length == 0
  628. if (advice_is_empty || jQuery(advice).hasClass(Vanadium.empty_advice_marker_class)) {
  629. jQuery(advice).addClass(Vanadium.empty_advice_marker_class);
  630. jQuery(advice).append("<span>" + validation_result.message + "</span>");
  631. }
  632. ;
  633. jQuery(advice).show();
  634. } else {
  635. advice = self.create_advice(validation_result);
  636. }
  637. ;
  638. } else {
  639. advice = self.create_advice(validation_result);
  640. }
  641. ;
  642. Vanadium.addValidationClass(advice, false);
  643. });
  644. },
  645. validateAndDecorate : function(regard_virginity) {
  646. //That's tricky one ;)
  647. // 1. we are runing validate to get all validation results
  648. // 2. there could be possible some validations running asynchronous
  649. // so we won't get the result imediately. In that case the provided decoration callback
  650. // will be invoked on return from asynchronous callback
  651. // It is used by Ajax based server-side validation
  652. if(!regard_virginity || !this.virgin)
  653. this.decorate(this.validate(this, this.decorate));
  654. },
  655. create_advice: function(validation_result) {
  656. var span = document.createElement("span");
  657. this.created_advices.push(span);
  658. jQuery(span).addClass(Vanadium.config.advice_class);
  659. jQuery(span).html(validation_result.message);
  660. jQuery(this.element).after(span);
  661. return span;
  662. },
  663. reset: function() {
  664. this.invalid = undefined; //mark this validation element as undefined
  665. // this.element_containers().each(function(_element, container) {
  666. // container.decorate();
  667. // });
  668. var element_advice = document.getElementById(this.advice_id);
  669. if (element_advice) {
  670. if (jQuery(element_advice).hasClass(Vanadium.empty_advice_marker_class)) {
  671. jQuery(element_advice).empty();
  672. }
  673. jQuery(element_advice).hide();
  674. }
  675. Vanadium.each(this.validations, function() {
  676. var advice = document.getElementById(this.adviceId);
  677. if (advice) {
  678. if (jQuery(advice).hasClass(Vanadium.empty_advice_marker_class)) {
  679. jQuery(advice).empty();
  680. }
  681. jQuery(advice).hide();
  682. }
  683. ;
  684. });
  685. var created_advice = this.created_advices.pop();
  686. while (!(created_advice === undefined)) {
  687. jQuery(created_advice).remove();
  688. created_advice = this.created_advices.pop();
  689. }
  690. ;
  691. Vanadium.removeValidationClass(this.element);
  692. },
  693. //
  694. //
  695. //
  696. /**
  697. * makes the validation wait the alotted time from the last keystroke
  698. */
  699. deferValidation: function() {
  700. if (this.wait >= 300) this.reset();
  701. var self = this;
  702. if (self.timeout) clearTimeout(self.timeout);
  703. self.timeout = setTimeout(function() {
  704. jQuery(self.element).trigger('validate');
  705. }, self.wait);
  706. },
  707. deferReset: function() {
  708. var self = this;
  709. if (self.reset_timeout) clearTimeout(self.reset_timeout);
  710. self.reset_timeout = setTimeout(function() {
  711. self.reset();
  712. }, Vanadium.config.reset_defer_timeout);
  713. },
  714. setup: function() {
  715. var self = this;
  716. this.elementType = Vanadium.getElementType(this.element);
  717. this.form = this.element.form;
  718. this.element_containers();
  719. if (!this.only_on_submit) {
  720. this.observe();
  721. jQuery(self.element).bind('validate', function() {
  722. self.validateAndDecorate.call(self, true)
  723. });
  724. jQuery(self.element).bind('defer_validation', function() {
  725. self.deferValidation.call(self)
  726. });
  727. jQuery(self.element).bind('reset', function() {
  728. self.reset.call(self)
  729. });
  730. }
  731. },
  732. observe: function() {
  733. var element = this.element;
  734. var elementType = Vanadium.getElementType(element);
  735. var self = this;
  736. jQuery(element).focus(function() {
  737. self.virgin = false;
  738. });
  739. switch (elementType) {
  740. case Vanadium.CHECKBOX:
  741. jQuery(element).click(function() {
  742. //TODO check db click !!!
  743. self.virgin = false; //this is here 'cos safari do not focus on checkboxes
  744. jQuery(self.element).trigger('validate');
  745. });
  746. break;
  747. //TODO check if checkboxes support on-change too. and if yes handle it!
  748. // let it run into the next to add a change event too
  749. case Vanadium.SELECT:
  750. case Vanadium.FILE:
  751. jQuery(element).change(function() {
  752. jQuery(element).trigger('validate');
  753. });
  754. break;
  755. default:
  756. jQuery(element).keydown(function(e) {
  757. if (e.keyCode != 9) {//no tabulation as it changes focus
  758. jQuery(element).trigger('reset');
  759. }
  760. ;
  761. });
  762. if (!this.only_on_blur) {
  763. jQuery(element).keyup(function(e) {
  764. if (e.keyCode != 9) {//no tabulation as it changes focus
  765. jQuery(element).trigger('defer_validation');
  766. }
  767. ;
  768. });
  769. };
  770. jQuery(element).blur(function() {
  771. jQuery(element).trigger('validate');
  772. });
  773. }
  774. }
  775. };
  776. //********** vanadium-instance.js **********
  777. var Validation = function(element, validation_type, param, advice_id) {
  778. this.initialize(element, validation_type, param, advice_id)
  779. }
  780. Validation.prototype = {
  781. initialize: function(element, validation_type, param, advice_id) {
  782. this.element = element;
  783. this.validation_type = validation_type;
  784. this.param = param;
  785. //
  786. this.adviceId = advice_id;
  787. var advice = document.getElementById(advice_id);
  788. if (advice) {
  789. jQuery(advice).addClass(Vanadium.config.advice_class);
  790. }
  791. if(this.validation_type.init){//Vanadium.isFunction(this.validation_type.init)){
  792. this.validation_type.init(this); //this give us oportunity to define in validation_type scope activity which will be performed on its instance initialisation
  793. }
  794. },
  795. emmit_message: function(message) {
  796. if (typeof(message) === "string") {
  797. return message;
  798. } else if (typeof(message) === "function") {
  799. return message.call(this, jQuery(this.element).val(), this.param);
  800. }
  801. },
  802. validMessage: function() {
  803. return this.emmit_message(this.validation_type.validMessage()) || 'ok'
  804. },
  805. invalidMessage: function() {
  806. return this.emmit_message(this.validation_type.invalidMessage()) || 'error'
  807. },
  808. test: function(decoration_context, decoration_callback) {
  809. return this.validation_type.validationFunction.call(this, jQuery(this.element).val(), this.param, this, decoration_context, decoration_callback);
  810. },
  811. // decoration_context - the contect in which decoration_callback should be invoked
  812. // decoration_callback - the decoration used by asynchronous validation
  813. validate: function(decoration_context, decoration_callback) {
  814. var return_value = {
  815. success: false,
  816. message: "Received invalid return value."
  817. }
  818. var validation_result = this.test(decoration_context, decoration_callback);
  819. if (typeof validation_result === "boolean") {
  820. return {
  821. success: validation_result,
  822. advice_id: this.adviceId,
  823. message: (validation_result ? this.validMessage() : this.invalidMessage())
  824. }
  825. } else if (typeof validation_result === "object") {
  826. jQuery.extend.apply(return_value, validation_result);
  827. }
  828. return return_value;
  829. }
  830. }
  831. //********** vanadium-init.js **********
  832. jQuery(document).ready(function () {
  833. if (typeof(VanadiumConfig) === "object" && VanadiumConfig) {
  834. Vanadium.each(VanadiumConfig, function(k, v) {
  835. Vanadium.config[k] = v;
  836. })
  837. }
  838. if (typeof(VanadiumRules) === "object" && VanadiumRules) {
  839. Vanadium.each(VanadiumRules, function(k, v) {
  840. Vanadium.rules[k] = v;
  841. })
  842. }
  843. Vanadium.init();
  844. });