nb.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. function nbInit($s){
  2. function nBind(callback, prop, direction){
  3. direction = direction || "write";
  4. for (var selector in $s){
  5. var result = [];
  6. selector = prop || selector; //change selector to passed if it
  7. var items = [document.getElementById(selector)];
  8. items = items[0] ? items : document.querySelectorAll(selector);
  9. items = items.length ? items : document.getElementsByName(selector);
  10. items = items.length ? items : document.getElementsByClassName(selector);
  11. for (var i=0,item=items[i];i<items.length;i++,item=items[i]){
  12. if (direction == "write" && item.children.length == 0 && Array.isArray($s[selector])){
  13. callback(item, $s, selector, $s[selector][i]);
  14. }
  15. else {
  16. var res = callback(item, $s, selector);
  17. if (typeof res !== "undefined"){
  18. result.push(res)
  19. }
  20. }
  21. }
  22. $s[selector] = result.length ? (result.length == 1 ? result[0] : result) : $s[selector];
  23. if (prop) return; //exit if selector passed, no iteration
  24. }
  25. }
  26. function syncToDOM(prop){
  27. nBind(function (item, $s, selector, value, key){
  28. value = typeof value === 'undefined' ? $s[selector] : value;
  29. var keyExists = typeof key !== 'undefined';
  30. if (!item.children.length && !Array.isArray(value) && typeof value === 'object'){ //hash array on single leaf node -> set attrs on the tag
  31. for (var key in value){
  32. item[key] = value[key];
  33. }
  34. return;
  35. }
  36. if (keyExists && "value" in item){ //if hash key-value pair. Usable for select > option
  37. item.value = key;
  38. }
  39. if (typeof value === "boolean" && item.type !== 'checkbox'){ //boolean means visibility, except checkbox
  40. if (value){
  41. item.style.display = "originalDisplay" in item ? item.originalDisplay : "";
  42. }
  43. else {
  44. item.originalDisplay = item.style.display;
  45. item.style.display = "none";
  46. }
  47. return;
  48. }
  49. if (item.type === 'radio' && !keyExists){ //radiogroup set
  50. if (item.value === value){ //only item with right value to set
  51. item.checked = true;
  52. }
  53. return;
  54. }
  55. if (item.type === 'checkbox' && !keyExists){ //checkbox setting by boolean
  56. item.checked = !!value;
  57. return;
  58. }
  59. if (item.children.length && typeof value === "object"){ //recursive fill
  60. item.copy = item.copy || item.cloneNode(true); //original node
  61. var originalChildren = item.copy.children;
  62. var i = 0;
  63. var isArray = Array.isArray(value); //different logic for array and objects
  64. if (!isArray){ // if first key in array find as class name in one of subnodes
  65. var classFound = false;
  66. for (var key in value){
  67. if (item.getElementsByClassName(key).length){
  68. classFound = true;
  69. break;
  70. }
  71. }
  72. if (classFound){
  73. for (var key in value){
  74. var classSubnodes = item.getElementsByClassName(key);
  75. for (var i=0;i<classSubnodes.length;i++){
  76. arguments.callee(classSubnodes[i], $s, selector, value[key]); // recursively fill subnode with that data. No reason to pass a key, because key are class selector, not value for option
  77. }
  78. }
  79. return;
  80. }
  81. }
  82. item.innerHTML = ""; //remove sub nodes
  83. for (var key in value){ //otherwise iterate over array or object
  84. var newNode = originalChildren[i].cloneNode(true);
  85. item.appendChild(newNode);
  86. if (isArray){
  87. arguments.callee(newNode, $s, selector, value[key]);
  88. }
  89. else {
  90. arguments.callee(newNode, $s, selector, value[key], key);
  91. }
  92. i = (i +1) % originalChildren.length;
  93. }
  94. return;
  95. }
  96. if (!keyExists){ //default logic: set text or value to data value
  97. item["value" in item ? "value" : "innerText"] = value;
  98. }
  99. else {
  100. item.innerText = value; // do not try to overwrite value on option nodes
  101. }
  102. },prop, "write");
  103. }
  104. function syncFromDOM(prop){
  105. nBind(function(item, $s, selector){
  106. if (item.type === 'radio'){
  107. if (item.checked)
  108. return item.value;
  109. return;
  110. }
  111. if (item.type === 'checkbox'){
  112. return item.checked;
  113. }
  114. return item["value" in item ? "value" : "innerText"];
  115. },prop, "read");
  116. }
  117. syncToDOM();
  118. var scopeProxy = new Proxy($s,{
  119. get(target, prop){
  120. if (!(prop in target) && (document.getElementById(prop) ||
  121. document.querySelectorAll(prop).length ||
  122. document.getElementsByName(prop).length ||
  123. document.getElementsByClassName(prop).length)){
  124. target[prop] = null;
  125. }
  126. syncFromDOM(prop);
  127. return target[prop];
  128. },
  129. set(target, prop, value){
  130. //syncFromDOM();
  131. target[prop] = value
  132. syncToDOM(prop);
  133. return true;
  134. },
  135. })
  136. return scopeProxy;
  137. }