nb.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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" && Array.isArray($s[selector]) && (item.children.length == 0 || (items.length == $s[selector].length))){
  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. item.nbData = value;
  35. return;
  36. }
  37. if (keyExists && "value" in item){ //if hash key-value pair. Usable for select > option
  38. item.value = key;
  39. }
  40. if (typeof value === "boolean" && item.type !== 'checkbox'){ //boolean means visibility, except checkbox
  41. if (value){
  42. item.style.display = "originalDisplay" in item ? item.originalDisplay : "";
  43. }
  44. else {
  45. item.originalDisplay = item.style.display;
  46. item.style.display = "none";
  47. }
  48. return;
  49. }
  50. if (item.type === 'radio' && !keyExists){ //radiogroup set
  51. if (item.value === value){ //only item with right value to set
  52. item.checked = true;
  53. }
  54. return;
  55. }
  56. if (item.type === 'checkbox' && !keyExists){ //checkbox setting by boolean
  57. item.checked = !!value;
  58. return;
  59. }
  60. if (item.children.length && typeof value === "object"){ //recursive fill
  61. item.copy = item.copy || item.cloneNode(true); //original node
  62. item.nbData = value;
  63. var originalChildren = item.copy.children;
  64. var i = 0;
  65. var isArray = Array.isArray(value); //different logic for array and objects
  66. if (!isArray){ // if first key in array find as class name in one of subnodes
  67. var classFound = false;
  68. for (var key in value){
  69. if (item.getElementsByClassName(key).length){
  70. classFound = true;
  71. break;
  72. }
  73. }
  74. if (classFound){
  75. for (var key in value){
  76. var classSubnodes = item.getElementsByClassName(key);
  77. for (var i=0;i<classSubnodes.length;i++){
  78. 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
  79. }
  80. }
  81. return;
  82. }
  83. }
  84. item.innerHTML = ""; //remove sub nodes
  85. for (var key in value){ //otherwise iterate over array or object
  86. var newNode = originalChildren[i].cloneNode(true);
  87. item.appendChild(newNode);
  88. if (isArray){
  89. arguments.callee(newNode, $s, selector, value[key]);
  90. }
  91. else {
  92. arguments.callee(newNode, $s, selector, value[key], key);
  93. }
  94. i = (i +1) % originalChildren.length;
  95. }
  96. return;
  97. }
  98. if (!keyExists){ //default logic: set text or value to data value
  99. item["value" in item ? "value" : "innerText"] = value;
  100. }
  101. else {
  102. item.innerText = value; // do not try to overwrite value on option nodes
  103. }
  104. },prop, "write");
  105. }
  106. function syncFromDOM(prop){
  107. nBind(function(item, $s, selector){
  108. if (item.type === 'radio'){
  109. if (item.checked)
  110. return item.value;
  111. return;
  112. }
  113. if (item.type === 'checkbox'){
  114. return item.checked;
  115. }
  116. if (item.tagName === 'SELECT'){
  117. return item.value;
  118. }
  119. if ("nbData" in item){
  120. var value = item.nbData;
  121. var isArray = Array.isArray(value); //different logic for array and objects
  122. if (item.children.length && typeof value === "object"){ //recursive fill
  123. if (!isArray){ // if first key in array find as class name in one of subnodes
  124. var classFound = false;
  125. for (var key in value){
  126. if (item.getElementsByClassName(key).length){
  127. classFound = true;
  128. break;
  129. }
  130. }
  131. if (classFound){
  132. for (var key in value){
  133. var classSubnodes = item.getElementsByClassName(key);
  134. for (var i=0;i<classSubnodes.length;i++){
  135. value[key] = arguments.callee(classSubnodes[i], $s, selector); // recursively fill subnode with that data. No reason to pass a key, because key are class selector, not value for option
  136. }
  137. }
  138. return value;
  139. }
  140. }
  141. else{
  142. value.length = 0;
  143. for (var key=0;key<item.children.length;key++){ //otherwise iterate over array or object
  144. value[key] = arguments.callee(item.children[key], $s, selector);
  145. }
  146. return value;
  147. }
  148. //else {
  149. //for (var key in value){
  150. //value[key] = arguments.callee(item.children[key], $s, selector);
  151. //}
  152. //}
  153. }
  154. if (!isArray && typeof value === 'object' && item.children.length === 0){ //hash array on single leaf node -> set attrs on the tag
  155. for (var key in value){
  156. value[key] = item[key];
  157. }
  158. return value;
  159. }
  160. if (!isArray && typeof value === 'object'){ //hash array on single leaf node -> set attrs on the tag
  161. value = {};
  162. for (var i=0;i<item.children.length;i++){
  163. var childItem = item.children[i];
  164. value[childItem.value] = childItem.innerText;
  165. }
  166. return value;
  167. }
  168. }
  169. if ("value" in item){
  170. return item.value;
  171. }
  172. return item.innerText;
  173. },prop, "read");
  174. }
  175. syncToDOM();
  176. var scopeProxy = new Proxy($s,{
  177. get(target, prop){
  178. //if (!(prop in target) && (document.getElementById(prop) ||
  179. //document.querySelectorAll(prop).length ||
  180. //document.getElementsByName(prop).length ||
  181. //document.getElementsByClassName(prop).length)){
  182. target[prop] = null;
  183. //}
  184. syncFromDOM(prop);
  185. return target[prop];
  186. },
  187. set(target, prop, value){
  188. //syncFromDOM();
  189. target[prop] = value
  190. syncToDOM(prop);
  191. return true;
  192. },
  193. })
  194. return scopeProxy;
  195. }