Browse Source

add example -- postgresql queries without pool, erl19 fix warnings

221V 1 year ago
parent
commit
3aa6cd1888
59 changed files with 1757 additions and 561 deletions
  1. 4 0
      .gitignore
  2. 4 4
      Makefile
  3. 5 0
      apps/n2o_sample/priv/css/test.css
  4. 1 0
      apps/n2o_sample/priv/js/n2o/BigInteger.min.js
  5. 36 0
      apps/n2o_sample/priv/js/n2o/bullet.js
  6. 98 0
      apps/n2o_sample/priv/js/n2o/ftp.js
  7. 79 0
      apps/n2o_sample/priv/js/n2o/http.js
  8. 96 0
      apps/n2o_sample/priv/js/n2o/n2o.js
  9. 281 0
      apps/n2o_sample/priv/js/n2o/protocols/bert.js
  10. 17 0
      apps/n2o_sample/priv/js/n2o/protocols/client.js
  11. 66 0
      apps/n2o_sample/priv/js/n2o/protocols/nitro.js
  12. 23 0
      apps/n2o_sample/priv/js/n2o/template.js
  13. 12 0
      apps/n2o_sample/priv/js/n2o/utf8.js
  14. 22 0
      apps/n2o_sample/priv/js/n2o/validation.js
  15. 23 0
      apps/n2o_sample/priv/js/n2o/xhr.js
  16. 44 0
      apps/n2o_sample/priv/js/testcities.js
  17. 0 45
      apps/n2o_sample/priv/static/spa/index.htm
  18. 50 0
      apps/n2o_sample/priv/static/spa/index.html
  19. 0 50
      apps/n2o_sample/priv/static/spa/login.htm
  20. 56 0
      apps/n2o_sample/priv/static/spa/login.html
  21. 0 26
      apps/n2o_sample/priv/templates/dev.html
  22. 0 47
      apps/n2o_sample/priv/templates/doc.html
  23. 55 0
      apps/n2o_sample/priv/templates/index.dtl
  24. 0 37
      apps/n2o_sample/priv/templates/index.html
  25. 31 0
      apps/n2o_sample/priv/templates/layout.dtl
  26. 56 0
      apps/n2o_sample/priv/templates/login.dtl
  27. 0 47
      apps/n2o_sample/priv/templates/login.html
  28. 7 0
      apps/n2o_sample/priv/templates/message.dtl
  29. 0 3
      apps/n2o_sample/priv/templates/message.html
  30. 28 0
      apps/n2o_sample/priv/templates/null.dtl
  31. 10 0
      apps/n2o_sample/priv/templates/testcities.dtl
  32. 5 5
      apps/n2o_sample/rebar.config
  33. 6 1
      apps/n2o_sample/src/config.erl
  34. 0 53
      apps/n2o_sample/src/doc.erl
  35. 22 0
      apps/n2o_sample/src/hg.erl
  36. 76 0
      apps/n2o_sample/src/hm.erl
  37. 24 15
      apps/n2o_sample/src/index.erl
  38. 0 93
      apps/n2o_sample/src/interlogin.erl
  39. 20 8
      apps/n2o_sample/src/login.erl
  40. 0 9
      apps/n2o_sample/src/n2o_sample.app.src
  41. 0 37
      apps/n2o_sample/src/n2o_sample.erl
  42. 24 0
      apps/n2o_sample/src/n2o_sample_app.erl
  43. 49 0
      apps/n2o_sample/src/n2o_sample_sup.erl
  44. 9 0
      apps/n2o_sample/src/n4u_sample.app.src
  45. 79 0
      apps/n2o_sample/src/pgm.erl
  46. 33 0
      apps/n2o_sample/src/pq.erl
  47. 7 6
      apps/n2o_sample/src/routes.erl
  48. 67 0
      apps/n2o_sample/src/testcities.erl
  49. 17 0
      apps/n2o_sample/src/tr_en.erl
  50. 17 0
      apps/n2o_sample/src/tr_uk.erl
  51. 5 0
      apps/n2o_sample/src/users.erl
  52. 12 0
      apps/n2o_sample/test_pg.sql
  53. BIN
      mad
  54. 97 0
      note.txt
  55. 16 35
      otp.mk
  56. 19 11
      rebar.config
  57. 35 27
      sys.config
  58. 12 0
      test_pg.sql
  59. 2 2
      vm.args

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+/deps/
+/apps/*/ebin/
+/rels/
+/Mnesia.n4u_sample@127.0.0.1/

+ 4 - 4
Makefile

@@ -1,7 +1,7 @@
-RELEASE := n2o_sample
-COOKIE  := node_runner
-VER     := 1.0.0
+PROJECT := n4u_sample
+COOKIE  := WwdHMhvbkJ3tVSLpF8GPRTQ5Cexj7Ama
+VER     := 0.1.0
 
 default: compile
-
 include otp.mk
+

+ 5 - 0
apps/n2o_sample/priv/css/test.css

@@ -0,0 +1,5 @@
+
+
+html, body{margin:0;padding:0;}
+.container{margin-top:15px;margin-left:20px;}
+#cities > p{ border-bottom: 1px green solid; }

+ 1 - 0
apps/n2o_sample/priv/js/n2o/BigInteger.min.js

@@ -0,0 +1 @@
+var bigInt=function(t){"use strict";var e=1e7,r=7,o=9007199254740992,n=v(o),i="0123456789abcdefghijklmnopqrstuvwxyz",u="function"==typeof BigInt;function p(t,e,r,o){return void 0===t?p[0]:void 0!==e&&(10!=+e||r)?_(t,e,r,o):K(t)}function a(t,e){this.value=t,this.sign=e,this.isSmall=!1}function s(t){this.value=t,this.sign=t<0,this.isSmall=!0}function l(t){this.value=t}function f(t){return-o<t&&t<o}function v(t){return t<1e7?[t]:t<1e14?[t%1e7,Math.floor(t/1e7)]:[t%1e7,Math.floor(t/1e7)%1e7,Math.floor(t/1e14)]}function h(t){y(t);var r=t.length;if(r<4&&A(t,n)<0)switch(r){case 0:return 0;case 1:return t[0];case 2:return t[0]+t[1]*e;default:return t[0]+(t[1]+t[2]*e)*e}return t}function y(t){for(var e=t.length;0===t[--e];);t.length=e+1}function g(t){for(var e=new Array(t),r=-1;++r<t;)e[r]=0;return e}function c(t){return t>0?Math.floor(t):Math.ceil(t)}function m(t,r){var o,n,i=t.length,u=r.length,p=new Array(i),a=0,s=e;for(n=0;n<u;n++)a=(o=t[n]+r[n]+a)>=s?1:0,p[n]=o-a*s;for(;n<i;)a=(o=t[n]+a)===s?1:0,p[n++]=o-a*s;return a>0&&p.push(a),p}function d(t,e){return t.length>=e.length?m(t,e):m(e,t)}function b(t,r){var o,n,i=t.length,u=new Array(i),p=e;for(n=0;n<i;n++)o=t[n]-p+r,r=Math.floor(o/p),u[n]=o-r*p,r+=1;for(;r>0;)u[n++]=r%p,r=Math.floor(r/p);return u}function w(t,r){var o,n,i=t.length,u=r.length,p=new Array(i),a=0,s=e;for(o=0;o<u;o++)(n=t[o]-a-r[o])<0?(n+=s,a=1):a=0,p[o]=n;for(o=u;o<i;o++){if(!((n=t[o]-a)<0)){p[o++]=n;break}n+=s,p[o]=n}for(;o<i;o++)p[o]=t[o];return y(p),p}function S(t,r,o){var n,i,u=t.length,p=new Array(u),l=-r,f=e;for(n=0;n<u;n++)i=t[n]+l,l=Math.floor(i/f),i%=f,p[n]=i<0?i+f:i;return"number"==typeof(p=h(p))?(o&&(p=-p),new s(p)):new a(p,o)}function I(t,r){var o,n,i,u,p=t.length,a=r.length,s=g(p+a),l=e;for(i=0;i<p;++i){u=t[i];for(var f=0;f<a;++f)o=u*r[f]+s[i+f],n=Math.floor(o/l),s[i+f]=o-n*l,s[i+f+1]+=n}return y(s),s}function q(t,r){var o,n,i=t.length,u=new Array(i),p=e,a=0;for(n=0;n<i;n++)o=t[n]*r+a,a=Math.floor(o/p),u[n]=o-a*p;for(;a>0;)u[n++]=a%p,a=Math.floor(a/p);return u}function M(t,e){for(var r=[];e-- >0;)r.push(0);return r.concat(t)}function N(t,r,o){return new a(t<e?q(r,t):I(r,v(t)),o)}function E(t){var r,o,n,i,u=t.length,p=g(u+u),a=e;for(n=0;n<u;n++){o=0-(i=t[n])*i;for(var s=n;s<u;s++)r=i*t[s]*2+p[n+s]+o,o=Math.floor(r/a),p[n+s]=r-o*a;p[n+u]=o}return y(p),p}function O(t,r){var o,n,i,u,p=t.length,a=g(p),s=e;for(i=0,o=p-1;o>=0;--o)i=(u=i*s+t[o])-(n=c(u/r))*r,a[o]=0|n;return[a,0|i]}function B(t,r){var o,n=K(r);if(u)return[new l(t.value/n.value),new l(t.value%n.value)];var i,f=t.value,m=n.value;if(0===m)throw new Error("Cannot divide by zero");if(t.isSmall)return n.isSmall?[new s(c(f/m)),new s(f%m)]:[p[0],t];if(n.isSmall){if(1===m)return[t,p[0]];if(-1==m)return[t.negate(),p[0]];var d=Math.abs(m);if(d<e){i=h((o=O(f,d))[0]);var b=o[1];return t.sign&&(b=-b),"number"==typeof i?(t.sign!==n.sign&&(i=-i),[new s(i),new s(b)]):[new a(i,t.sign!==n.sign),new s(b)]}m=v(d)}var S=A(f,m);if(-1===S)return[p[0],t];if(0===S)return[p[t.sign===n.sign?1:-1],p[0]];i=(o=f.length+m.length<=200?function(t,r){var o,n,i,u,p,a,s,l=t.length,f=r.length,v=e,y=g(r.length),c=r[f-1],m=Math.ceil(v/(2*c)),d=q(t,m),b=q(r,m);for(d.length<=l&&d.push(0),b.push(0),c=b[f-1],n=l-f;n>=0;n--){for(o=v-1,d[n+f]!==c&&(o=Math.floor((d[n+f]*v+d[n+f-1])/c)),i=0,u=0,a=b.length,p=0;p<a;p++)i+=o*b[p],s=Math.floor(i/v),u+=d[n+p]-(i-s*v),i=s,u<0?(d[n+p]=u+v,u=-1):(d[n+p]=u,u=0);for(;0!==u;){for(o-=1,i=0,p=0;p<a;p++)(i+=d[n+p]-v+b[p])<0?(d[n+p]=i+v,i=0):(d[n+p]=i,i=1);u+=i}y[n]=o}return d=O(d,m)[0],[h(y),h(d)]}(f,m):function(t,r){for(var o,n,i,u,p,a=t.length,s=r.length,l=[],f=[],v=e;a;)if(f.unshift(t[--a]),y(f),A(f,r)<0)l.push(0);else{i=f[(n=f.length)-1]*v+f[n-2],u=r[s-1]*v+r[s-2],n>s&&(i=(i+1)*v),o=Math.ceil(i/u);do{if(A(p=q(r,o),f)<=0)break;o--}while(o);l.push(o),f=w(f,p)}return l.reverse(),[h(l),h(f)]}(f,m))[0];var I=t.sign!==n.sign,M=o[1],N=t.sign;return"number"==typeof i?(I&&(i=-i),i=new s(i)):i=new a(i,I),"number"==typeof M?(N&&(M=-M),M=new s(M)):M=new a(M,N),[i,M]}function A(t,e){if(t.length!==e.length)return t.length>e.length?1:-1;for(var r=t.length-1;r>=0;r--)if(t[r]!==e[r])return t[r]>e[r]?1:-1;return 0}function P(t){var e=t.abs();return!e.isUnit()&&(!!(e.equals(2)||e.equals(3)||e.equals(5))||!(e.isEven()||e.isDivisibleBy(3)||e.isDivisibleBy(5))&&(!!e.lesser(49)||void 0))}function Z(t,e){for(var r,o,n,i=t.prev(),u=i,p=0;u.isEven();)u=u.divide(2),p++;t:for(o=0;o<e.length;o++)if(!t.lesser(e[o])&&!(n=bigInt(e[o]).modPow(u,t)).isUnit()&&!n.equals(i)){for(r=p-1;0!=r;r--){if((n=n.square().mod(t)).isUnit())return!1;if(n.equals(i))continue t}return!1}return!0}a.prototype=Object.create(p.prototype),s.prototype=Object.create(p.prototype),l.prototype=Object.create(p.prototype),a.prototype.add=function(t){var e=K(t);if(this.sign!==e.sign)return this.subtract(e.negate());var r=this.value,o=e.value;return e.isSmall?new a(b(r,Math.abs(o)),this.sign):new a(d(r,o),this.sign)},a.prototype.plus=a.prototype.add,s.prototype.add=function(t){var e=K(t),r=this.value;if(r<0!==e.sign)return this.subtract(e.negate());var o=e.value;if(e.isSmall){if(f(r+o))return new s(r+o);o=v(Math.abs(o))}return new a(b(o,Math.abs(r)),r<0)},s.prototype.plus=s.prototype.add,l.prototype.add=function(t){return new l(this.value+K(t).value)},l.prototype.plus=l.prototype.add,a.prototype.subtract=function(t){var e=K(t);if(this.sign!==e.sign)return this.add(e.negate());var r=this.value,o=e.value;return e.isSmall?S(r,Math.abs(o),this.sign):function(t,e,r){var o;return A(t,e)>=0?o=w(t,e):(o=w(e,t),r=!r),"number"==typeof(o=h(o))?(r&&(o=-o),new s(o)):new a(o,r)}(r,o,this.sign)},a.prototype.minus=a.prototype.subtract,s.prototype.subtract=function(t){var e=K(t),r=this.value;if(r<0!==e.sign)return this.add(e.negate());var o=e.value;return e.isSmall?new s(r-o):S(o,Math.abs(r),r>=0)},s.prototype.minus=s.prototype.subtract,l.prototype.subtract=function(t){return new l(this.value-K(t).value)},l.prototype.minus=l.prototype.subtract,a.prototype.negate=function(){return new a(this.value,!this.sign)},s.prototype.negate=function(){var t=this.sign,e=new s(-this.value);return e.sign=!t,e},l.prototype.negate=function(){return new l(-this.value)},a.prototype.abs=function(){return new a(this.value,!1)},s.prototype.abs=function(){return new s(Math.abs(this.value))},l.prototype.abs=function(){return new l(this.value>=0?this.value:-this.value)},a.prototype.multiply=function(t){var r,o,n,i=K(t),u=this.value,s=i.value,l=this.sign!==i.sign;if(i.isSmall){if(0===s)return p[0];if(1===s)return this;if(-1===s)return this.negate();if((r=Math.abs(s))<e)return new a(q(u,r),l);s=v(r)}return o=u.length,n=s.length,new a(-.012*o-.012*n+15e-6*o*n>0?function t(e,r){var o=Math.max(e.length,r.length);if(o<=30)return I(e,r);o=Math.ceil(o/2);var n=e.slice(o),i=e.slice(0,o),u=r.slice(o),p=r.slice(0,o),a=t(i,p),s=t(n,u),l=t(d(i,n),d(p,u)),f=d(d(a,M(w(w(l,a),s),o)),M(s,2*o));return y(f),f}(u,s):I(u,s),l)},a.prototype.times=a.prototype.multiply,s.prototype._multiplyBySmall=function(t){return f(t.value*this.value)?new s(t.value*this.value):N(Math.abs(t.value),v(Math.abs(this.value)),this.sign!==t.sign)},a.prototype._multiplyBySmall=function(t){return 0===t.value?p[0]:1===t.value?this:-1===t.value?this.negate():N(Math.abs(t.value),this.value,this.sign!==t.sign)},s.prototype.multiply=function(t){return K(t)._multiplyBySmall(this)},s.prototype.times=s.prototype.multiply,l.prototype.multiply=function(t){return new l(this.value*K(t).value)},l.prototype.times=l.prototype.multiply,a.prototype.square=function(){return new a(E(this.value),!1)},s.prototype.square=function(){var t=this.value*this.value;return f(t)?new s(t):new a(E(v(Math.abs(this.value))),!1)},l.prototype.square=function(t){return new l(this.value*this.value)},a.prototype.divmod=function(t){var e=B(this,t);return{quotient:e[0],remainder:e[1]}},l.prototype.divmod=s.prototype.divmod=a.prototype.divmod,a.prototype.divide=function(t){return B(this,t)[0]},l.prototype.over=l.prototype.divide=function(t){return new l(this.value/K(t).value)},s.prototype.over=s.prototype.divide=a.prototype.over=a.prototype.divide,a.prototype.mod=function(t){return B(this,t)[1]},l.prototype.mod=l.prototype.remainder=function(t){return new l(this.value%K(t).value)},s.prototype.remainder=s.prototype.mod=a.prototype.remainder=a.prototype.mod,a.prototype.pow=function(t){var e,r,o,n=K(t),i=this.value,u=n.value;if(0===u)return p[1];if(0===i)return p[0];if(1===i)return p[1];if(-1===i)return n.isEven()?p[1]:p[-1];if(n.sign)return p[0];if(!n.isSmall)throw new Error("The exponent "+n.toString()+" is too large.");if(this.isSmall&&f(e=Math.pow(i,u)))return new s(c(e));for(r=this,o=p[1];!0&u&&(o=o.times(r),--u),0!==u;)u/=2,r=r.square();return o},s.prototype.pow=a.prototype.pow,l.prototype.pow=function(t){var e=K(t),r=this.value,o=e.value,n=BigInt(0),i=BigInt(1),u=BigInt(2);if(o===n)return p[1];if(r===n)return p[0];if(r===i)return p[1];if(r===BigInt(-1))return e.isEven()?p[1]:p[-1];if(e.isNegative())return new l(n);for(var a=this,s=p[1];(o&i)===i&&(s=s.times(a),--o),o!==n;)o/=u,a=a.square();return s},a.prototype.modPow=function(t,e){if(t=K(t),(e=K(e)).isZero())throw new Error("Cannot take modPow with modulus 0");var r=p[1],o=this.mod(e);for(t.isNegative()&&(t=t.multiply(p[-1]),o=o.modInv(e));t.isPositive();){if(o.isZero())return p[0];t.isOdd()&&(r=r.multiply(o).mod(e)),t=t.divide(2),o=o.square().mod(e)}return r},l.prototype.modPow=s.prototype.modPow=a.prototype.modPow,a.prototype.compareAbs=function(t){var e=K(t),r=this.value,o=e.value;return e.isSmall?1:A(r,o)},s.prototype.compareAbs=function(t){var e=K(t),r=Math.abs(this.value),o=e.value;return e.isSmall?r===(o=Math.abs(o))?0:r>o?1:-1:-1},l.prototype.compareAbs=function(t){var e=this.value,r=K(t).value;return(e=e>=0?e:-e)===(r=r>=0?r:-r)?0:e>r?1:-1},a.prototype.compare=function(t){if(t===1/0)return-1;if(t===-1/0)return 1;var e=K(t),r=this.value,o=e.value;return this.sign!==e.sign?e.sign?1:-1:e.isSmall?this.sign?-1:1:A(r,o)*(this.sign?-1:1)},a.prototype.compareTo=a.prototype.compare,s.prototype.compare=function(t){if(t===1/0)return-1;if(t===-1/0)return 1;var e=K(t),r=this.value,o=e.value;return e.isSmall?r==o?0:r>o?1:-1:r<0!==e.sign?r<0?-1:1:r<0?1:-1},s.prototype.compareTo=s.prototype.compare,l.prototype.compare=function(t){if(t===1/0)return-1;if(t===-1/0)return 1;var e=this.value,r=K(t).value;return e===r?0:e>r?1:-1},l.prototype.compareTo=l.prototype.compare,a.prototype.equals=function(t){return 0===this.compare(t)},l.prototype.eq=l.prototype.equals=s.prototype.eq=s.prototype.equals=a.prototype.eq=a.prototype.equals,a.prototype.notEquals=function(t){return 0!==this.compare(t)},l.prototype.neq=l.prototype.notEquals=s.prototype.neq=s.prototype.notEquals=a.prototype.neq=a.prototype.notEquals,a.prototype.greater=function(t){return this.compare(t)>0},l.prototype.gt=l.prototype.greater=s.prototype.gt=s.prototype.greater=a.prototype.gt=a.prototype.greater,a.prototype.lesser=function(t){return this.compare(t)<0},l.prototype.lt=l.prototype.lesser=s.prototype.lt=s.prototype.lesser=a.prototype.lt=a.prototype.lesser,a.prototype.greaterOrEquals=function(t){return this.compare(t)>=0},l.prototype.geq=l.prototype.greaterOrEquals=s.prototype.geq=s.prototype.greaterOrEquals=a.prototype.geq=a.prototype.greaterOrEquals,a.prototype.lesserOrEquals=function(t){return this.compare(t)<=0},l.prototype.leq=l.prototype.lesserOrEquals=s.prototype.leq=s.prototype.lesserOrEquals=a.prototype.leq=a.prototype.lesserOrEquals,a.prototype.isEven=function(){return 0==(1&this.value[0])},s.prototype.isEven=function(){return 0==(1&this.value)},l.prototype.isEven=function(){return(this.value&BigInt(1))===BigInt(0)},a.prototype.isOdd=function(){return 1==(1&this.value[0])},s.prototype.isOdd=function(){return 1==(1&this.value)},l.prototype.isOdd=function(){return(this.value&BigInt(1))===BigInt(1)},a.prototype.isPositive=function(){return!this.sign},s.prototype.isPositive=function(){return this.value>0},l.prototype.isPositive=s.prototype.isPositive,a.prototype.isNegative=function(){return this.sign},s.prototype.isNegative=function(){return this.value<0},l.prototype.isNegative=s.prototype.isNegative,a.prototype.isUnit=function(){return!1},s.prototype.isUnit=function(){return 1===Math.abs(this.value)},l.prototype.isUnit=function(){return this.abs().value===BigInt(1)},a.prototype.isZero=function(){return!1},s.prototype.isZero=function(){return 0===this.value},l.prototype.isZero=function(){return this.value===BigInt(0)},a.prototype.isDivisibleBy=function(t){var e=K(t);return!e.isZero()&&(!!e.isUnit()||(0===e.compareAbs(2)?this.isEven():this.mod(e).isZero()))},l.prototype.isDivisibleBy=s.prototype.isDivisibleBy=a.prototype.isDivisibleBy,a.prototype.isPrime=function(t){var e=P(this);if(void 0!==e)return e;var r=this.abs(),o=r.bitLength();if(o<=64)return Z(r,[2,3,5,7,11,13,17,19,23,29,31,37]);for(var n=Math.log(2)*o.toJSNumber(),i=Math.ceil(!0===t?2*Math.pow(n,2):n),u=[],p=0;p<i;p++)u.push(bigInt(p+2));return Z(r,u)},l.prototype.isPrime=s.prototype.isPrime=a.prototype.isPrime,a.prototype.isProbablePrime=function(t,e){var r=P(this);if(void 0!==r)return r;for(var o=this.abs(),n=void 0===t?5:t,i=[],u=0;u<n;u++)i.push(bigInt.randBetween(2,o.minus(2),e));return Z(o,i)},l.prototype.isProbablePrime=s.prototype.isProbablePrime=a.prototype.isProbablePrime,a.prototype.modInv=function(t){for(var e,r,o,n=bigInt.zero,i=bigInt.one,u=K(t),p=this.abs();!p.isZero();)e=u.divide(p),r=n,o=u,n=i,u=p,i=r.subtract(e.multiply(i)),p=o.subtract(e.multiply(p));if(!u.isUnit())throw new Error(this.toString()+" and "+t.toString()+" are not co-prime");return-1===n.compare(0)&&(n=n.add(t)),this.isNegative()?n.negate():n},l.prototype.modInv=s.prototype.modInv=a.prototype.modInv,a.prototype.next=function(){var t=this.value;return this.sign?S(t,1,this.sign):new a(b(t,1),this.sign)},s.prototype.next=function(){var t=this.value;return t+1<o?new s(t+1):new a(n,!1)},l.prototype.next=function(){return new l(this.value+BigInt(1))},a.prototype.prev=function(){var t=this.value;return this.sign?new a(b(t,1),!0):S(t,1,this.sign)},s.prototype.prev=function(){var t=this.value;return t-1>-o?new s(t-1):new a(n,!0)},l.prototype.prev=function(){return new l(this.value-BigInt(1))};for(var x=[1];2*x[x.length-1]<=e;)x.push(2*x[x.length-1]);var J=x.length,L=x[J-1];function U(t){return Math.abs(t)<=e}function T(t,e,r){e=K(e);for(var o=t.isNegative(),n=e.isNegative(),i=o?t.not():t,u=n?e.not():e,p=0,a=0,s=null,l=null,f=[];!i.isZero()||!u.isZero();)p=(s=B(i,L))[1].toJSNumber(),o&&(p=L-1-p),a=(l=B(u,L))[1].toJSNumber(),n&&(a=L-1-a),i=s[0],u=l[0],f.push(r(p,a));for(var v=0!==r(o?1:0,n?1:0)?bigInt(-1):bigInt(0),h=f.length-1;h>=0;h-=1)v=v.multiply(L).add(bigInt(f[h]));return v}a.prototype.shiftLeft=function(t){var e=K(t).toJSNumber();if(!U(e))throw new Error(String(e)+" is too large for shifting.");if(e<0)return this.shiftRight(-e);var r=this;if(r.isZero())return r;for(;e>=J;)r=r.multiply(L),e-=J-1;return r.multiply(x[e])},l.prototype.shiftLeft=s.prototype.shiftLeft=a.prototype.shiftLeft,a.prototype.shiftRight=function(t){var e,r=K(t).toJSNumber();if(!U(r))throw new Error(String(r)+" is too large for shifting.");if(r<0)return this.shiftLeft(-r);for(var o=this;r>=J;){if(o.isZero()||o.isNegative()&&o.isUnit())return o;o=(e=B(o,L))[1].isNegative()?e[0].prev():e[0],r-=J-1}return(e=B(o,x[r]))[1].isNegative()?e[0].prev():e[0]},l.prototype.shiftRight=s.prototype.shiftRight=a.prototype.shiftRight,a.prototype.not=function(){return this.negate().prev()},l.prototype.not=s.prototype.not=a.prototype.not,a.prototype.and=function(t){return T(this,t,function(t,e){return t&e})},l.prototype.and=s.prototype.and=a.prototype.and,a.prototype.or=function(t){return T(this,t,function(t,e){return t|e})},l.prototype.or=s.prototype.or=a.prototype.or,a.prototype.xor=function(t){return T(this,t,function(t,e){return t^e})},l.prototype.xor=s.prototype.xor=a.prototype.xor;var j=1<<30,C=(e&-e)*(e&-e)|j;function D(t){var r=t.value,o="number"==typeof r?r|j:"bigint"==typeof r?r|BigInt(j):r[0]+r[1]*e|C;return o&-o}function z(t,e){return t=K(t),e=K(e),t.greater(e)?t:e}function R(t,e){return t=K(t),e=K(e),t.lesser(e)?t:e}function k(t,e){if(t=K(t).abs(),e=K(e).abs(),t.equals(e))return t;if(t.isZero())return e;if(e.isZero())return t;for(var r,o,n=p[1];t.isEven()&&e.isEven();)r=R(D(t),D(e)),t=t.divide(r),e=e.divide(r),n=n.multiply(r);for(;t.isEven();)t=t.divide(D(t));do{for(;e.isEven();)e=e.divide(D(e));t.greater(e)&&(o=e,e=t,t=o),e=e.subtract(t)}while(!e.isZero());return n.isUnit()?t:t.multiply(n)}a.prototype.bitLength=function(){var t=this;return t.compareTo(bigInt(0))<0&&(t=t.negate().subtract(bigInt(1))),0===t.compareTo(bigInt(0))?bigInt(0):bigInt(function t(e,r){if(r.compareTo(e)<=0){var o=t(e,r.square(r)),n=o.p,i=o.e,u=n.multiply(r);return u.compareTo(e)<=0?{p:u,e:2*i+1}:{p:n,e:2*i}}return{p:bigInt(1),e:0}}(t,bigInt(2)).e).add(bigInt(1))},l.prototype.bitLength=s.prototype.bitLength=a.prototype.bitLength;var _=function(t,e,r,o){r=r||i,t=String(t),o||(t=t.toLowerCase(),r=r.toLowerCase());var n,u=t.length,p=Math.abs(e),a={};for(n=0;n<r.length;n++)a[r[n]]=n;for(n=0;n<u;n++){if("-"!==(f=t[n])&&(f in a&&a[f]>=p)){if("1"===f&&1===p)continue;throw new Error(f+" is not a valid digit in base "+e+".")}}e=K(e);var s=[],l="-"===t[0];for(n=l?1:0;n<t.length;n++){var f;if((f=t[n])in a)s.push(K(a[f]));else{if("<"!==f)throw new Error(f+" is not a valid character");var v=n;do{n++}while(">"!==t[n]&&n<t.length);s.push(K(t.slice(v+1,n)))}}return $(s,e,l)};function $(t,e,r){var o,n=p[0],i=p[1];for(o=t.length-1;o>=0;o--)n=n.add(t[o].times(i)),i=i.times(e);return r?n.negate():n}function F(t,e){if((e=bigInt(e)).isZero()){if(t.isZero())return{value:[0],isNegative:!1};throw new Error("Cannot convert nonzero numbers to base 0.")}if(e.equals(-1)){if(t.isZero())return{value:[0],isNegative:!1};if(t.isNegative())return{value:[].concat.apply([],Array.apply(null,Array(-t.toJSNumber())).map(Array.prototype.valueOf,[1,0])),isNegative:!1};var r=Array.apply(null,Array(t.toJSNumber()-1)).map(Array.prototype.valueOf,[0,1]);return r.unshift([1]),{value:[].concat.apply([],r),isNegative:!1}}var o=!1;if(t.isNegative()&&e.isPositive()&&(o=!0,t=t.abs()),e.isUnit())return t.isZero()?{value:[0],isNegative:!1}:{value:Array.apply(null,Array(t.toJSNumber())).map(Number.prototype.valueOf,1),isNegative:o};for(var n,i=[],u=t;u.isNegative()||u.compareAbs(e)>=0;){n=u.divmod(e),u=n.quotient;var p=n.remainder;p.isNegative()&&(p=e.minus(p).abs(),u=u.next()),i.push(p.toJSNumber())}return i.push(u.toJSNumber()),{value:i.reverse(),isNegative:o}}function G(t,e,r){var o=F(t,e);return(o.isNegative?"-":"")+o.value.map(function(t){return function(t,e){return t<(e=e||i).length?e[t]:"<"+t+">"}(t,r)}).join("")}function H(t){if(f(+t)){var e=+t;if(e===c(e))return u?new l(BigInt(e)):new s(e);throw new Error("Invalid integer: "+t)}var o="-"===t[0];o&&(t=t.slice(1));var n=t.split(/e/i);if(n.length>2)throw new Error("Invalid integer: "+n.join("e"));if(2===n.length){var i=n[1];if("+"===i[0]&&(i=i.slice(1)),(i=+i)!==c(i)||!f(i))throw new Error("Invalid integer: "+i+" is not a valid exponent.");var p=n[0],v=p.indexOf(".");if(v>=0&&(i-=p.length-v-1,p=p.slice(0,v)+p.slice(v+1)),i<0)throw new Error("Cannot include negative exponent part for integers");t=p+=new Array(i+1).join("0")}if(!/^([0-9][0-9]*)$/.test(t))throw new Error("Invalid integer: "+t);if(u)return new l(BigInt(o?"-"+t:t));for(var h=[],g=t.length,m=r,d=g-m;g>0;)h.push(+t.slice(d,g)),(d-=m)<0&&(d=0),g-=m;return y(h),new a(h,o)}function K(t){return"number"==typeof t?function(t){if(u)return new l(BigInt(t));if(f(t)){if(t!==c(t))throw new Error(t+" is not an integer.");return new s(t)}return H(t.toString())}(t):"string"==typeof t?H(t):"bigint"==typeof t?new l(t):t}a.prototype.toArray=function(t){return F(this,t)},s.prototype.toArray=function(t){return F(this,t)},l.prototype.toArray=function(t){return F(this,t)},a.prototype.toString=function(t,e){if(void 0===t&&(t=10),10!==t)return G(this,t,e);for(var r,o=this.value,n=o.length,i=String(o[--n]);--n>=0;)r=String(o[n]),i+="0000000".slice(r.length)+r;return(this.sign?"-":"")+i},s.prototype.toString=function(t,e){return void 0===t&&(t=10),10!=t?G(this,t,e):String(this.value)},l.prototype.toString=s.prototype.toString,l.prototype.toJSON=a.prototype.toJSON=s.prototype.toJSON=function(){return this.toString()},a.prototype.valueOf=function(){return parseInt(this.toString(),10)},a.prototype.toJSNumber=a.prototype.valueOf,s.prototype.valueOf=function(){return this.value},s.prototype.toJSNumber=s.prototype.valueOf,l.prototype.valueOf=l.prototype.toJSNumber=function(){return parseInt(this.toString(),10)};for(var Q=0;Q<1e3;Q++)p[Q]=K(Q),Q>0&&(p[-Q]=K(-Q));return p.one=p[1],p.zero=p[0],p.minusOne=p[-1],p.max=z,p.min=R,p.gcd=k,p.lcm=function(t,e){return t=K(t).abs(),e=K(e).abs(),t.divide(k(t,e)).multiply(e)},p.isInstance=function(t){return t instanceof a||t instanceof s||t instanceof l},p.randBetween=function(t,r,o){t=K(t),r=K(r);var n=o||Math.random,i=R(t,r),u=z(t,r).subtract(i).add(1);if(u.isSmall)return i.add(Math.floor(n()*u));for(var a=F(u,e).value,s=[],l=!0,f=0;f<a.length;f++){var v=l?a[f]:e,h=c(n()*v);s.push(h),h<v&&(l=!1)}return i.add(p.fromArray(s,e,!1))},p.fromArray=function(t,e,r){return $(t.map(K),K(e||10),r)},p}();"undefined"!=typeof module&&module.hasOwnProperty("exports")&&(module.exports=bigInt),"function"==typeof define&&define.amd&&define(function(){return bigInt});

+ 36 - 0
apps/n2o_sample/priv/js/n2o/bullet.js

@@ -0,0 +1,36 @@
+
+// WebSocket Transport
+
+$ws = { heart: true, interval: 5000,
+        creator: function(url) { return window.WebSocket ? new window.WebSocket(url) : false; },
+        onheartbeat: function() { this.channel.send('PING'); } };
+
+// N2O Reliable Connection
+
+$conn = { onopen: nop, onmessage: nop, onclose: nop, onconnect: nop,
+          send:  function(data)   { if (this.port.channel) this.port.channel.send(data); },
+          close: function()       { if (this.port.channel) this.port.channel.close(); } };
+
+ct = 0;
+transports = [ $ws ];
+heartbeat = null;
+reconnectDelay = 1000;
+maxReconnects = 100;
+
+function nop() { }
+function bullet(url) { $conn.url = url; return $conn; }
+function xport() { return maxReconnects <= ct ? false : transports[ct++ % transports.length]; }
+function reconnect() { setTimeout(function() { connect(); }, reconnectDelay); }
+function next() { $conn.port = xport(); return $conn.port ? connect() : false; }
+function connect() {
+    $conn.port.channel = $conn.port.creator($conn.url);
+    if (!$conn.port.channel) return next();
+    $conn.port.channel.binaryType = "arraybuffer";
+    $conn.port.channel.onmessage = function(e) { $conn.onmessage(e); };
+    $conn.port.channel.onopen = function() {
+        if ($conn.port.heart) heartbeat = setInterval(function(){$conn.port.onheartbeat();}, $conn.port.interval);
+        $conn.onopen();
+        $conn.onconnect(); };
+    $conn.port.channel.onclose = function() { $conn.onclose(); clearInterval(heartbeat); reconnect(); };
+    return $conn; }
+

+ 98 - 0
apps/n2o_sample/priv/js/n2o/ftp.js

@@ -0,0 +1,98 @@
+try { module.exports = {ftp:ftp}; } catch (e) { }
+
+// N2O File Transfer Protocol
+
+var ftp = {
+    queue: [],
+    init:  function(file) {
+        var item = {
+            id:        performance.now().toString(),
+            status:    'init',
+            status_block_id: ftp.status_block_id || 'ftp_status',
+            autostart: ftp.autostart || false,
+            name:      ftp.filename || file.name,
+            sid:       ftp.sid || co(session),
+            meta:      ftp.meta || bin(''),
+            other1:    ftp.other1 || bin(''),
+            other2:    ftp.other2 || bin(''),
+            other3:    ftp.other3 || bin(''),
+            offset:    ftp.offset || 0,
+            block:     1,
+            total:     file.size,
+            file:      file
+        };
+        ftp.queue.push(item);
+        ftp.send(item, '', 1);
+        return item.id;
+    },
+    start: function(id) {
+        if(ftp.active) { id && ( ftp.item(id).autostart = true ); return false; }
+        var item = id ? ftp.item(id) : ftp.next();
+        if(item) { ftp.active = true; ftp.send_slice(item); }
+    },
+    stop:  function(id) {
+        var item = ftp.item(id);
+        var index = ftp.queue.indexOf(item);
+        ftp.queue.splice(index, 1);
+        ftp.active = false;
+        ftp.start();
+    },
+    send:  function(item, data) {
+        if(debug){
+          console.log('item ', item);
+          console.log('data ', data);
+        }
+        ws.send(enc(tuple(atom('ftp'),
+            bin(item.id),
+            bin(item.sid),
+            bin(item.name),
+            item.meta,
+            item.other1,
+            item.other2,
+            item.other3,
+            number(item.total),
+            number(item.offset),
+            number(item.block || data.byteLength),
+            bin(data),
+            bin(item.status || 'send')
+            ))); },
+    send_slice: function(item) {
+        this.reader = new FileReader();
+        this.reader.onloadend = function(e) {
+            var res = e.target, data = e.target.result;
+            if(res.readyState === FileReader.DONE && data.byteLength > 0) ftp.send(item,data);
+         };
+        this.reader.readAsArrayBuffer(item.file.slice(item.offset, item.offset + item.block)); },
+    item: function(id) { return ftp.queue.find(function(item){ return item && item.id === id; }); },
+    next: function() { return ftp.queue.find(function(next){ return next && next.autostart }); }
+};
+
+$file.do = function(rsp) {
+    if(debug){ console.log('rsp ', rsp); }
+    var offset = rsp.v[9].v,
+        block = rsp.v[10].v,
+        //status = utf8_dec(rsp.v[12].v);
+        status = utf8_arr(rsp.v[12].v);
+    switch (status) {
+        case 'init':
+            //var item = ftp.item(utf8_dec(rsp.v[1].v));
+            var item = ftp.item(utf8_arr(rsp.v[1].v));
+            item.offset = offset;
+            item.block = block;
+            //item.name = utf8_dec(rsp.v[3].v);
+            item.name = utf8_arr(rsp.v[3].v);
+            item.status = undefined;
+            if(item.autostart) ftp.start(item.id);
+            break;
+        case 'send':
+            //var item = ftp.item(utf8_dec(rsp.v[1].v));
+            var item = ftp.item(utf8_arr(rsp.v[1].v));
+            var x = qi(item.status_block_id); if(x) x.innerHTML = offset;
+            item.offset = offset;
+            item.block = block;
+            (block > 0 && ftp.active) ? ftp.send_slice(item) : ftp.stop(item.id)
+            break;
+        case 'relay': if (typeof ftp.relay === 'function') ftp.relay(rsp); break;
+    }
+};
+

+ 79 - 0
apps/n2o_sample/priv/js/n2o/http.js

@@ -0,0 +1,79 @@
+try { module.exports = {http:http}; } catch (e) { }
+
+// Template: http.send(url + '?' + 'test1=1&test2=2', 'GET', '', {SomeHeader:'some header'}).done(function(data, status, headers) {console.log(data););
+
+var http = {
+    receiveData:null,
+    doneCallback:function(){},
+    failCallback:function(){},
+    settings:{},
+    send: function(settings) {
+        var $ = this;
+        var defSett = {
+            url:window.location.href,
+            method:'GET',
+            body:'',
+            headers:{},
+            returnType:'html',
+        };
+        $.settings = {};
+        if (typeof settings === 'string') {
+            $.settings = $.merge(defSett, {url:settings});
+        } else {
+            $.settings = $.merge(defSett, settings);
+        }
+        if (/^\/\//.test($.settings.url)) {
+            $.settings.url = window.location.protocol + $.settings.url
+        }
+        if (!/^http[s]*\:\/\//.test($.settings.url)) {
+            $.settings.url = window.location.origin + $.settings.url
+        }
+        var tList = [];
+        if ($.settings.headers) {
+            for (var prop in $.settings.headers) {
+                tList.push(tuple(bin(prop),bin($.settings.headers[prop])));
+            }
+        }
+        ws.send(enc(tuple(atom('http'),
+            bin($.settings.url),
+            bin($.settings.method||'GET'),
+            bin($.settings.body||''),
+            tList)
+        ));
+        return $;
+    },
+    back: function(d, s, h) {
+        if (s >= 400) {
+            this.failCallback(d, s, h);
+        } else {
+            if (this.settings.returnType === 'json') {
+                try {
+                    d = JSON.parse(this.escapeSpecialChars(d));
+                } catch (e) {
+                    if (debug) console.error('Unexpected string for JSON');
+                };
+            }
+            this.doneCallback(d, s, h);
+        }
+    },
+    done: function(callback) {
+        this.doneCallback = callback;
+        return this;
+    },
+    fail: function(callback) {
+        this.failCallback = callback;
+        return this;
+    },
+    merge: function(obj, src) {
+        var newO = {};
+        Object.keys(obj).forEach(function(key){newO[key] = obj[key];});
+        Object.keys(src).forEach(function(key){newO[key] = src[key];});
+        return newO;
+    },
+    escapeSpecialChars: function(jsonString) {
+        return jsonString.replace(/\n/g, "\\n")
+            .replace(/\r/g, "\\r")
+            .replace(/\t/g, "\\t")
+            .replace(/\f/g, "\\f");
+    }
+};

+ 96 - 0
apps/n2o_sample/priv/js/n2o/n2o.js

@@ -0,0 +1,96 @@
+
+// N2O CORE
+
+var active      = false,
+    debug       = false,
+    session     = "site-sid",
+    protocol    = window.location.protocol == 'https:' ? "wss://" : "ws://",
+    querystring = window.location.pathname + window.location.search,
+    host        = window.location.hostname,
+    port        = window.location.hostname == 'localhost' ? window.location.port : transition.port;
+
+function N2O_start(){
+  ws = new bullet(protocol + host + (port == "" ? "" : ":" + port) + "/ws" + querystring);
+  ws.onmessage = function(evt){ // formatters loop
+    for(var i = 0;i < protos.length; i++){ p = protos[i]; if(p.on(evt, p.do).status == "ok") return; }
+  };
+  ws.onopen = function(){
+    if(!active){
+      console.log('Connect');
+      ws.send('N2O,' + transition.pid);
+      active = true;
+    }
+  };
+  ws.onclose = function(){
+    active = false;
+    console.log('Disconnect');
+  };
+  next();
+}
+
+function qi(name){ return document.getElementById(name); }
+function qs(name){ return document.querySelector(name); }
+function qa(name){ return document.querySelectorAll(name); }
+function qn(name){ return document.createElement(name); }
+function is(x, num, name){ return x == undefined ? false : (x.t == 106 ? false : (x.v.length === num && x.v[0].v === name)); }
+function co(name){ match = document.cookie.match(new RegExp(name + '=([^;]+)')); return match ? match[1] : undefined; }
+
+/// N2O Protocols
+
+var $io = {};
+$io.on = function onio(r, cb){
+  if(is(r, 3, 'io')){
+    if(typeof cb == 'function') cb(r);
+    var evalex = utf8_arr(r.v[1].v);
+    try{
+      console.log("from n2o.js:46 \n", evalex);
+      eval(evalex);
+      return { status: "ok" };
+    }catch(e){
+      console.error("Eval failed: \n", e);
+      return { status: '' };
+    }
+  }else return { status: '' };
+};
+
+var $file = {};
+$file.on = function onfile(r, cb){
+  //console.log('r ', r);
+  //console.log('is ', is(r,13,'ftp'));
+  if(is(r,13,'ftp')){
+    if(typeof cb == 'function') cb(r);
+    return { status: "ok" };
+  }else return { status: ''};
+};
+
+var $bin = {};
+$bin.on = function onbin(r, cb){
+  if(is(r,2,'bin')){
+    if(typeof cb == 'function') cb(r);
+    return { status: "ok" };
+  }else return { status: '' };
+};
+
+
+// BERT Formatter
+
+var $bert = {};
+$bert.protos = [$io, $bin, $file];
+$bert.on = function onbert(evt, cb){
+  if(ArrayBuffer.prototype.isPrototypeOf(evt.data) && (evt.data.byteLength > 0)){
+    try{
+      var erlang = dec(evt.data);
+      //console.log(JSON.stringify(erlang));
+      if(typeof cb  == 'function') cb(erlang);
+      for(var i = 0; i < $bert.protos.length; i++){
+        p = $bert.protos[i];
+        var ret = p.on(erlang, p.do);
+        if(ret != undefined && ret.status == "ok") return ret;
+      }
+    }catch(e){ console.error(e); }
+    return { status: "ok" };
+  }else return { status: "error", desc: "data" };
+};
+
+var protos = [ $bert ];
+

+ 281 - 0
apps/n2o_sample/priv/js/n2o/protocols/bert.js

@@ -0,0 +1,281 @@
+//try { module.exports = { dec: dec, enc: enc }; } catch (e) { console.log(e); }
+
+
+// API
+
+function tuple(){ return { t: 104, v: Array.apply(null, arguments) }; }
+function list(){ return { t: 108, v: Array.apply(null, arguments) }; }
+function map(){ return { t: 116, v: Array.apply(null, arguments) }; }
+//function atom(o){ return { t: 100, v: utf8_enc(o) }; }
+function atom(o){ return { t: 118, v: utf8_enc(o) }; }
+function string(o){ return { t: 107, v: utf8_enc(o) }; }
+function float(o){ return { t: 70, v: o }; }
+function number(o){
+  var isInteger = (o % 1 === 0);
+  if(isInteger && o >= 0 && o < 256){ return { t: 97, v: o }; }
+  //if(isInteger && o >= -134217728 && o <= 134217727){ return {t: 98, v: o}; }
+  if(isInteger && o >= -2147483648 && o <= 2147483647){ return {t: 98, v: o}; }
+  return {t: 110, v: o};
+}
+
+// BigInt to BERT, with https://github.com/peterolson/BigInteger.js
+function bignum(o){
+  if(bigInt.isInstance(o) === false){ return {t: 999, v: [97, 0]}; } // o is not bigInt
+  if(o.greaterOrEquals(0) && o.lesser(256)){
+    // t: 97
+    return {t: 999, v: [97, o.toJSNumber() ]};
+  }
+  if(o.greaterOrEquals(-2147483648) && o.lesserOrEquals(2147483647)){
+    // t: 98
+    return {t: 999, v: [98, o.shiftRight(24).toJSNumber(), o.shiftRight(16).and(255).toJSNumber(), o.shiftRight(8).and(255).toJSNumber(), o.and(255).toJSNumber() ]};
+  }
+  // t: 110
+  if(o.isNegative()){
+    var sign = 1;
+    var s = bignum_to_bytes(o.abs());
+  }else{
+    var sign = 0;
+    var s = bignum_to_bytes(o);
+  }
+  return {t: 999, v: [110, s.length, sign].concat(s) };
+}
+
+function bin(o){
+  return { t: 109, v: o instanceof ArrayBuffer ? new Uint8Array(o) :
+                      o instanceof Uint8Array ? o : utf8_enc(o) };
+}
+
+
+// encoder
+
+function enc(o){ return fl([131, ein(o)]); }
+function ein(o){
+  //console.log("from bert.js:31 \n", o);
+  return Array.isArray(o) ? en_108({ t: 108, v: o }) :
+                            (o.t == 999 ? o.v : eval('en_' + o.t)(o) ); // t: 999 = bigInt, already encoded in bignum func
+}
+function en_undefined(o){ return [106]; }
+function unilen(o){
+  return (o.v instanceof ArrayBuffer || o.v instanceof Uint8Array) ? o.v.byteLength :
+         (new TextEncoder().encode(o.v)).byteLength;
+}
+function en_70(o){
+  var x = Array(8).fill(0).flat();
+  write_Float(x, o.v, 0, false, 52, 8);
+  return [70].concat(x);
+}
+function en_97(o){ return [97, o.v]; }
+function en_98(o){ return [98, o.v >>> 24, (o.v >>> 16) & 255, (o.v >>> 8) & 255, o.v & 255]; }
+function en_99(o){
+  var obj = o.v.toExponential(20),
+      match = /([^e]+)(e[+-])(\d+)/.exec(obj),
+      exponentialPart = match[3].length == 1 ? "0" + match[3] : match[3],
+      num = Array.from(bin(match[1] + match[2] + exponentialPart).v);
+  return [o.t].concat(num).concat(Array(31 - num.length).fill(0).flat());
+}
+function en_100(o){ return [100, o.v.length >>> 8, o.v.length & 255, ar(o)]; }
+function en_104(o){
+  var l = o.v.length,
+      r = [];
+  for(var i = 0; i < l; i++) r[i] = ein(o.v[i]);
+  return [104, l, r];
+}
+function en_106(o){ return [106]; }
+function en_107(o){ return [107, o.v.length >>> 8, o.v.length & 255, ar(o)]; }
+function en_108(o){
+  var l = o.v.length,
+      r = [];
+  for(var i = 0; i < l; i++) r.push(ein(o.v[i]));
+  return o.v.length == 0 ? [106] :
+    [108, l >>> 24, (l >>> 16) & 255, (l >>> 8) & 255, l & 255, r, 106];
+}
+function en_109(o){
+  var l = unilen(o);
+  return [109, l >>> 24, (l >>> 16) & 255, (l >>> 8) & 255, l & 255, ar(o)];
+}
+function en_110(o){
+  if(o.v < 0){
+    var sign = 1;
+    var s = int_to_bytes(-o.v);
+  }else{
+    var sign = 0;
+    var s = int_to_bytes(o.v);
+  }
+  return [110, s.length, sign].concat(s);
+}
+function en_115(o){ return [115, o.v.length, ar(o)]; }
+function en_116(o){
+  var l = o.v.length,
+      x = [],
+      r = [];
+  for(var i = 0; i < l; i++) r.push([ein(o.v[i].k), ein(o.v[i].v)]);
+  x = [116, l >>> 24, (l >>> 16) & 255, (l >>> 8) & 255, l & 255];
+  return o.v.length == 0 ? x : [x, r];
+}
+function en_118(o){ return [118, ar(o).length >>> 8, ar(o).length & 255, ar(o)]; }
+function en_119(o){ return [119, ar(o).length, ar(o)]; }
+
+
+// decoder
+
+function nop(b){ return []; }
+function big(b){
+  var sk = b == 1 ? sx.getUint8(ix++) : sx.getInt32((a = ix, ix += 4, a));
+  var ret = 0,
+      sig = sx.getUint8(ix++),
+      count = sk;
+  while(count-- > 0){
+    ret = 256 * ret + sx.getUint8(ix + count);
+  }
+  ix += sk;
+  return ret * (sig == 0 ? 1 : -1);
+}
+function int(b){
+  return b == 1 ? sx.getUint8(ix++) : sx.getInt32((a = ix, ix += 4, a));
+}
+function dec(d){
+  sx = new DataView(d);
+  ix = 0;
+  if(sx.getUint8(ix++) !== 131) throw ("BERT?");
+  return din();
+}
+function str(b){
+  var dv,
+      sz = (b == 2 ? sx.getUint16(ix) : (b == 1 ? sx.getUint8(ix) : sx.getUint32(ix)));
+  ix += b;
+  var r = sx.buffer.slice(ix, ix += sz);
+  return utf8_arr(r);
+}
+function run(b){
+  var sz = (b == 1 ? sx.getUint8(ix) : sx.getUint32(ix)),
+      r = [];
+      ix += b;
+  for(var i = 0; i < sz; i++) r.push(din());
+  if(b == 4) ix++;
+  return r;
+}
+function rut(b){
+  var sz = (b == 1 ? sx.getUint8(ix) : sx.getUint32(ix)),
+      r = [];
+      ix += b;
+  for(var i = 0; i < sz; i++) r.push(din());
+  din();
+  return r;
+}
+function dic(b){
+  var sz = sx.getUint32(ix),
+      r = [];
+      ix += 4;
+  for(var i = 0; i < sz; i++) r.push({k: din(), v: din()});
+  return r;
+}
+function iee(x){
+  return read_Float(new Uint8Array(sx.buffer.slice(ix, ix += 8)), 0, false, 52, 8);
+}
+
+function flo(x){
+  return parseFloat(utf8_arr(sx.buffer.slice(ix, ix += 31)));
+}
+
+function arr(b){
+  var dv,
+      sz = sx.getUint16(ix);
+  ix += b;
+  return new Uint8Array(sx.buffer.slice(ix, ix += sz));
+}
+
+function ref(cr){
+  var d,
+      adj = sx.getUint8(ix++);
+  adj += sx.getUint8(ix++);
+  d = din();
+  ix += cr + adj * 4;
+  return d;
+}
+
+function din(){
+  var x,
+      c = sx.getUint8(ix++);
+  switch(c){
+    case  70: x = [iee, 0]; break;
+    case  90: x = [ref, 4]; break;
+    case  97: x = [int, 1]; break;
+    case  98: x = [int, 4]; break;
+    case  99: x = [flo, 0]; break;
+    case 100: x = [str, 2]; break;
+    case 104: x = [run, 1]; break;
+    case 105: x = [run, 4]; break;
+    case 107: x = [arr, 2]; break;
+    case 108: x = [rut, 4]; break;
+    case 109: x = [str, 4]; break;
+    case 110: x = [big, 1]; break;
+    case 111: x = [big, 4]; break;
+    case 114: x = [ref, 1]; break;
+    case 115: x = [str, 1]; break;
+    case 116: x = [dic, 4]; break;
+    case 118: x = [str, 2]; break;
+    case 119: x = [str, 1]; break;
+    default: x = [nop, 0];
+  } return { t: c, v: x[0](x[1]) };
+}
+
+
+// helpers
+
+function int_to_bytes(Int){
+  if(Int % 1 !== 0) return [0];
+      //var i,
+      //isNegative,
+  var OriginalInt,
+      Rem,
+      s = [];
+  //isNegative = (Int < 0);
+  //if(isNegative){ Int = - Int - 1; }
+  OriginalInt = Int;
+  //var length = 0;
+  while(Int !== 0){
+    Rem = Int % 256;
+    //if(isNegative){
+    //  Rem = 255 - Rem;
+    //}
+    s.push(Rem);
+    Int = Math.floor(Int / 256);
+    //length++;
+  }
+  if(Int > 0){ throw ("Argument out of range: " + OriginalInt); }
+  return s;
+}
+
+function bignum_to_bytes(big_Int){
+  var v,
+      big_Int,
+      s = [];
+  big_Int2 = big_Int;
+  while(big_Int2.isZero() === false){
+    v = big_Int2.divmod(256);
+    s.push(v.remainder.toJSNumber());
+    big_Int2 = v.quotient;
+  }
+  if(big_Int2.greater(0)){ throw ("Argument out of range::: " + big_Int.toString() ); }
+  return s;
+}
+
+function uc(u1, u2){
+  if(u1.byteLength == 0) return u2;
+  if(u2.byteLength == 0) return u1;
+  var a = new Uint8Array(u1.byteLength + u2.byteLength);
+  a.set(u1, 0);
+  a.set(u2, u1.byteLength);
+  return a;
+}
+function ar(o){
+  return o.v instanceof ArrayBuffer ? new Uint8Array(o.v) : o.v instanceof Uint8Array ? o.v :
+    Array.isArray(o.v) ? new Uint8Array(o.v) : new Uint8Array(utf8_enc(o.v));
+}
+function fl(a){
+  return a.reduce(function(f, t){
+    return uc(f, t instanceof Uint8Array ? t :
+      Array.isArray(t) ? fl(t) : new Uint8Array([t]));
+  }, new Uint8Array());
+}
+

+ 17 - 0
apps/n2o_sample/priv/js/n2o/protocols/client.js

@@ -0,0 +1,17 @@
+
+// JSON formatter
+
+var $client = {};
+$client.on = function onclient(evt, callback){
+  try{
+    msg = JSON.parse(evt.data);
+    if(debug) console.log(JSON.stringify(msg));
+    if(typeof callback == 'function' && msg) callback(msg);
+    for(var i = 0; i < $bert.protos.length; i++){
+      p = $bert.protos[i];
+      if(p.on(msg, p.do).status == "ok") return { status: "ok"};
+    }
+  }catch(e){ return { status: "error" }; }
+  return { status: "ok" };
+};
+

+ 66 - 0
apps/n2o_sample/priv/js/n2o/protocols/nitro.js

@@ -0,0 +1,66 @@
+// nitrogen compatibility layer
+
+function direct(term){ ws.send(enc(tuple(atom('direct'), term))); }
+function validateSources(){ return true; }
+function querySourceRaw(Id){
+  var val, el = document.getElementById(Id);
+  if(!el){
+    val = qs('input[name=' + Id + ']:checked');
+    val = val ? val.value : "";
+  }else switch(el.tagName){
+    case 'FIELDSET':
+      val = qs('[id="' + Id + '"]:checked');
+      val = val ? val.value : "";
+      break;
+    case 'INPUT':
+      switch(el.getAttribute("type")){
+        case 'radio':
+        case 'checkbox':
+          val = qs('input[name=' + Id + ']:checked');
+          val = val ? val.value : "";
+          break;
+        case 'date':
+          val = Date.parse(el.value);
+          val = val && new Date(val) || "";
+          break;
+        case 'calendar':
+          val = pickers[el.id]._d || "";
+          break;
+        default:
+          var edit = el.contentEditable;
+          if(edit && edit === 'true') val = el.innerHTML;
+          else val = el.value;
+      }
+      break;
+    default:
+      var edit = el.contentEditable;
+      if(edit && edit === 'true'){
+        val = el.innerHTML;
+      }else{
+        val = el.value;
+        switch(val){
+          case "true":  val = new Boolean(true);  break;
+          case "false": val = new Boolean(false); break;
+        }
+      }
+  }
+  return val;
+}
+
+function querySource(Id){
+  var qs = querySourceRaw(Id);
+  if(qs instanceof Date){
+    return tuple(number(qs.getFullYear()),
+                 number(qs.getMonth() + 1),
+                 number(qs.getDate()));
+  }else if(qs instanceof Boolean){
+    return atom(qs.valueOf());
+  }else{ return bin(qs); }
+}
+
+(function(){
+  window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
+    window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
+})();
+
+

+ 23 - 0
apps/n2o_sample/priv/js/n2o/template.js

@@ -0,0 +1,23 @@
+
+// N2O Simple Template
+
+function template(html, data) { // template("{this.name}",{name:"Maxim"})
+    var re = /{([^}]+)?}/g, code = 'var r=[];', cursor = 0;
+    var add = function(line,js) {
+        js? (code += 'r.push(' + line + ');') :
+            (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");' : ''); // "
+        return add; }
+    while(match = re.exec(html)) {
+        add(html.slice(cursor, match.index))(match[1],true);
+        cursor = match.index + match[0].length; }
+    add(html.substr(cursor, html.length - cursor));
+    code += 'return r.join("");';
+    return new Function(code.replace(/[\r\t\n]/g, '')).apply(data); }
+
+function xml(html) { return new DOMParser().parseFromString(html, "application/xhtml+xml").firstChild; }
+function dom(html) {
+    try { return new DOMParser().parseFromString(html, "text/html").firstChild.getElementsByTagName("body")[0].firstChild; } 
+    catch (ex) { var temp = document.createElement("DIV");
+                     temp.innerHTML = html;
+                     return temp.firstChild; }
+}

+ 12 - 0
apps/n2o_sample/priv/js/n2o/utf8.js

@@ -0,0 +1,12 @@
+//try { module.exports = {dec:utf8_dec, enc:utf8_enc}; } catch (e) { console.log(e); }
+
+
+// UTF-8 Support
+
+function utf8_dec(ab){ return (new TextDecoder()).decode(ab); }
+function utf8_enc(ab){ return (new TextEncoder("utf-8")).encode(ab); }
+function utf8_arr(ab){
+  if(!(ab instanceof ArrayBuffer)) ab = new Uint8Array(utf8_enc(ab)).buffer;
+  return utf8_dec(ab);
+}
+

+ 22 - 0
apps/n2o_sample/priv/js/n2o/validation.js

@@ -0,0 +1,22 @@
+
+// N2O Validation
+
+function validateSources(list) {
+    return list.reduce(function(acc,x) {
+        var event = new CustomEvent('validation');
+            event.initCustomEvent('validation',true,true,querySourceRaw(x));
+        var el = qi(x),
+            listener = el && el.validation,
+            res = !listener || listener && el.dispatchEvent(event);
+        console.log(res);
+        if (el) el.style.background = res ? '' : 'pink';
+        return res && acc; },true); }
+
+(function () {
+   function CustomEvent ( event, params ) {
+       params = params || { bubbles: false, cancelable: false, detail: undefined };
+       var evt = document.createEvent( 'CustomEvent' );
+       evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
+       return evt;  };
+  CustomEvent.prototype = window.Event.prototype;
+  window.CustomEvent = CustomEvent; })();

+ 23 - 0
apps/n2o_sample/priv/js/n2o/xhr.js

@@ -0,0 +1,23 @@
+
+// N2O XHR Fallback
+
+// WebSocket = undefined; // to test
+
+$xhr = { heart: false, interval: 100, creator: function(url) { $conn.url = xhr_url(url);
+         $xhr.channel = { send: xhr_send, close: xhr_close }; $conn.onopen();
+         return $xhr.channel; }, onheartbeat: function() { xhr('POST',{});} };
+
+transports = [$ws,$xhr];
+
+function xhr_header(request) { request.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=utf-8'); }
+function xhr_url(url) { return url.replace('ws:', 'http:').replace('wss:', 'https:'); }
+function xhr_close() { $conn.onclose(); clearInterval(heartbeat); }
+function xhr_send(data) { return xhr('POST',data); }
+function xhr_receive(data) { if (data.length != 0) $conn.onmessage({'data':data}); }
+function xhr(method,data) {
+    var request = new XMLHttpRequest();
+    request.open(method,$conn.url,true);
+    xhr_header(request);
+    request.onload = function() { console.log(request.response); xhr_receive(request.response); };
+    request.send(data);
+    return true; }

+ 44 - 0
apps/n2o_sample/priv/js/testcities.js

@@ -0,0 +1,44 @@
+
+function cityshow(){
+  var timerId = setTimeout(function tick(){
+      if(window.active){
+
+        if(window.getting_data !== true){
+          window.getting_data = true;
+
+          qi('citiesshow').disabled = true;
+          ws.send(enc(tuple( atom('client'), tuple(atom('sitiesshow') ) )));
+        }
+
+      }else{
+        timerId = setTimeout(tick, 200);
+      }
+  }, 100);
+}
+
+
+function cityadd(){
+  var timerId = setTimeout(function tick(){
+      if(window.active){
+
+        if(window.sending_data !== true){
+          window.sending_data = true;
+
+          qi('cityadd').disabled = true;
+          ws.send(enc(tuple( atom('client'), tuple(atom('cityadd'), querySource('cityname'), number(qi('citypop').value) ) )));
+        }
+
+      }else{
+        timerId = setTimeout(tick, 200);
+      }
+  }, 100);
+}
+
+
+window.addEventListener("load", function(){
+
+  qi('citiesshow').addEventListener("click", cityshow, false);
+  qi('cityadd').addEventListener("click", cityadd, false);
+
+},false);
+

+ 0 - 45
apps/n2o_sample/priv/static/spa/index.htm

@@ -1,45 +0,0 @@
-<html>
-    <head>
-        <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
-        <link href='/static/synrc.css' type='text/css' rel='stylesheet'>
-        <link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
-        <title>Chat</title>
-    </head>
-    <body>
-        <table cellpadding='20' align='center'>
-            <tr>
-                <td valign='top' align='center' height='230' width='300'>
-                    <br><img src='/static/S.svg' border='0' height='200' style='margin-top:0px;'><br>
-                </td>
-                <td colspan='2' width='700' valign='top'>
-                    <h1><b id='heading'>Roster</b></h1>
-                    <h2>Unique Feed<br><button id='logout'></button></h2>
-                </td>
-            </tr>
-            <tr>
-                <td width='300' valign='top'>
-                    <p>Just type what you think about this:</p>
-                    <textarea id='message' style='margin-left:5px; width:490px; font-size:16pt;'
-                      value='' rows='5' autofocus></textarea>
-                    <div id='upload'></div>
-                    <div id='history'></div>
-                    <button id=send>Send</body>
-                </td>
-            </tr>
-        </table>
-        <script>var transition = {pid: '', host: 'localhost', port:'8000'};</script>
-        <script src='/n2o/protocols/bert.js'></script>
-        <script src='/n2o/protocols/binary.js'></script>
-        <script src='/n2o/protocols/client.js'></script>
-        <script src='/n2o/protocols/nitrogen.js'></script>
-        <script src='/n2o/bullet.js'></script>
-        <script src='/n2o/xhr.js'></script>
-        <script src='/n2o/utf8.js'></script>
-        <script src='/n2o/template.js'></script>
-        <script src='/n2o/n2o.js'></script>
-        <script src='/n2o/validation.js'></script>
-        <script src='/n2o/ftp.js'></script>
-        <!--script src='https://synrc.com/hi.js'></script-->
-        <script>protos = [$client,$bert]; N2O_start();</script>
-    </body>
-</html>

+ 50 - 0
apps/n2o_sample/priv/static/spa/index.html

@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link href="/static/synrc.css" rel="stylesheet">
+<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
+<title>Chat</title>
+</head>
+<body>
+
+<table cellpadding="20" align="center">
+  <tr>
+    <td valign="top" align="center" height="230" width="300">
+      <br><img src="/static/S.svg" border="0" height="200" style="margin-top:0px;"><br>
+    </td>
+    <td colspan="2" width="700" valign="top">
+      <h1><b id="heading">Roster</b></h1>
+      <h2>Unique Feed<br><button id="logout"></button></h2>
+    </td>
+  </tr>
+  <tr>
+    <td width="300" valign="top">
+      <p>Just type what you think about this:</p>
+      <textarea id="message" style="margin-left:5px; width:490px; font-size:16pt;" value="" rows="5" autofocus></textarea>
+      <div id="upload"></div>
+      <div id="history"></div>
+      <button id="send">Send</body>
+    </td>
+  </tr>
+</table>
+
+<script>var transition = {pid: '', host: 'localhost', port:'8000'};</script>
+<script src="/js/n2o/protocols/bert.js" defer></script>
+<script src="/js/n2o/protocols/client.js" defer></script>
+<script src="/js/n2o/protocols/nitrogen.js" defer></script>
+<script src="/js/n2o/bullet.js" defer></script>
+<script src="/js/n2o/xhr.js" defer></script>
+<script src="/js/n2o/utf8.js" defer></script>
+<script src="/js/n2o/template.js" defer></script>
+<script src="/js/n2o/n2o.js" defer></script>
+<script src="/js/n2o/validation.js" defer></script>
+<script src="/js/n2o/ftp.js" defer></script>
+<script>
+  protos = [$client, $bert];
+  N2O_start();
+</script>
+
+</body>
+</html>

+ 0 - 50
apps/n2o_sample/priv/static/spa/login.htm

@@ -1,50 +0,0 @@
-<html>
-    <head>
-        <meta name='viewport' content='initial-scale=0.5'>
-        <link href='/static/synrc.css' type='text/css' rel='stylesheet'>
-        <link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
-        <title>Login</title>
-    </head>
-    <body>
-
-        <table cellpadding='20' align='center'>
-            <tr>
-                <td valign='top' align='center' height='230' width='300'>
-                    <img src='/static/S.svg' border='0' height='200' style='margin-top:0px;'><br>
-                </td>
-                <td colspan='2' width='700' valign='top'>
-                    <h1><b>N2O</b></h1>
-                    <h2>Simple Review Application</h2>
-                </td>
-            </tr>
-            <tr>
-                <td width='300' valign='top'>
-                    <p>List of spawned feeds:</p>
-                    <div id='history' class='feed'></div><br><br>
-                </td>
-                <td colspan='2' width='700' valign='top' height='550' bgcolor='#eeeeee'>
-                    <h1>Anyname Login</h1>
-                    <span id="display"></span><br>
-                    <span>Login: </span>
-                    <input id="user" type="text" autofocus="true"><br>
-                    <span>Join/Create Feed: </span>
-                    <input id="pass" type="text">
-                    <button id="loginButton" type="button">Login</button>
-                </td>
-                </td>
-            </tr>
-        </table>
-        <script>var transition = {pid: '', host: 'localhost', port:'8000'};</script>
-        <script src='/n2o/protocols/bert.js'></script>
-        <script src='/n2o/protocols/binary.js'></script>
-        <script src='/n2o/protocols/client.js'></script>
-        <script src='/n2o/protocols/nitrogen.js'></script>
-        <script src='/n2o/bullet.js'></script>
-        <script src='/n2o/xhr.js'></script>
-        <script src='/n2o/utf8.js'></script>
-        <script src='/n2o/template.js'></script>
-        <script src='/n2o/n2o.js'></script>
-        <script src='/n2o/validation.js'></script>
-        <script>protos = [$client,$bert]; N2O_start();</script>
-    </body>
-</html>

+ 56 - 0
apps/n2o_sample/priv/static/spa/login.html

@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=0.5">
+<link href="/static/synrc.css" rel="stylesheet">
+<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
+<title>Login</title>
+</head>
+<body>
+
+<table cellpadding="20" align="center">
+  <tr>
+    <td valign="top" align="center" height="230" width="300">
+      <img src="/static/S.svg" border="0" height="200" style="margin-top:0px;"><br>
+    </td>
+    <td colspan="2" width="700" valign="top">
+      <h1><b>N2O</b></h1>
+      <h2>Simple Review Application</h2>
+    </td>
+  </tr>
+  <tr>
+    <td width="300" valign="top">
+      <p>List of spawned feeds:</p>
+      <div id="history" class="feed"></div><br><br>
+    </td>
+    <td colspan="2" width="700" valign="top" height="550" bgcolor="#eeeeee">
+      <h1>Anyname Login</h1>
+      <span id="display"></span><br>
+      <span>Login: </span>
+      <input id="user" type="text" autofocus="true"><br>
+      <span>Join/Create Feed: </span>
+      <input id="pass" type="text">
+      <button id="loginButton" type="button">Login</button>
+    </td>
+  </tr>
+</table>
+
+<script>var transition = {pid: '', host: 'localhost', port:'8000'};</script>
+<script src="/js/n2o/protocols/bert.js" defer></script>
+<script src="/js/n2o/protocols/client.js" defer></script>
+<script src="/js/n2o/protocols/nitrogen.js" defer></script>
+<script src="/js/n2o/bullet.js" defer></script>
+<script src="/js/n2o/xhr.js" defer></script>
+<script src="/js/n2o/utf8.js" defer></script>
+<script src="/js/n2o/template.js" defer></script>
+<script src="/js/n2o/n2o.js" defer></script>
+<script src="/js/n2o/validation.js" defer></script>
+<script src="/js/n2o/ftp.js" defer></script>
+<script>
+  protos = [$client, $bert];
+  N2O_start();
+</script>
+
+</body>
+</html>

+ 0 - 26
apps/n2o_sample/priv/templates/dev.html

@@ -1,26 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
-  <meta name='viewport'  content="width=device-width, initial-scale=1.0">
-  <link href='http://fonts.googleapis.com/css?family=Lato:300'    rel='stylesheet' type='text/css'/>
-  <link href="https://unpkg.com/purecss@0.6.2/build/pure-min.css" rel="stylesheet" type='text/css'/>
-  <link href='/static/synrc.css'  rel='stylesheet' type='text/css'/>
-  <link href='/static/feed.css'   rel='stylesheet' type='text/css'/>
-  <title>{{title}}</title>
-</head>
-<body>
-  {{body}}
-  <script>{{script}}</script>
-  <script src='/n2o/protocols/bert.js'></script>
-  <script src='/n2o/protocols/client.js'></script>
-  <script src='/n2o/protocols/nitrogen.js'></script>
-  <script src='/n2o/bullet.js'></script>
-  <script src='/n2o/xhr.js'></script>
-  <script src='/n2o/utf8.js'></script>
-  <script src='/n2o/template.js'></script>
-  <script src='/n2o/n2o.js'></script>
-  <script src='/n2o/validation.js'></script>
-  <script src='/n2o/ftp.js'></script>
-  <script>protos = [$client,$bert]; N2O_start();</script>
-</body>
-</html>

+ 0 - 47
apps/n2o_sample/priv/templates/doc.html

@@ -1,47 +0,0 @@
-<html>
-    <head>
-        <meta name='viewport' content='initial-scale=0.5'>
-        <link href='/static/synrc.css' type='text/css' rel='stylesheet'>
-        <link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
-        <title>Login</title>
-    </head>
-    <body>
-
-        <table cellpadding='20' align='center'>
-            <tr>
-                <td valign='top' align='center' height='230' width='300'>
-                    <a href="/"><img src='/static/S.svg' border='0' height='200' style='margin-top:0px;'></a><br>
-                </td>
-                <td colspan='2' width='700' valign='top'>
-                    <h1><b>N2O</b></h1>
-                    <h2>Simple Review Application</h2>
-                </td>
-            </tr>
-            <tr>
-                <td width='300' valign='top'>
-                    <p>List of spawned feeds:</p>
-                    <div id='history' class='feed'></div><br><br>
-                    <p>List of inhabited realms:</p>
-                    <div class='feed'>
-                        {{folders}}
-                    </div>
-                </td>
-                <td colspan='2' width='700' valign='top' height='550' bgcolor='#eeeeee'>
-                    <h1>Anyname Login</h1>
-                    {{body}}
-                </td>
-            </tr>
-        </table>
-        <script>{{script}}</script>
-        <script src='/n2o/protocols/bert.js'></script>
-        <script src='/n2o/protocols/client.js'></script>
-        <script src='/n2o/protocols/nitrogen.js'></script>
-        <script src='/n2o/bullet.js'></script>
-        <script src='/n2o/xhr.js'></script>
-        <script src='/n2o/utf8.js'></script>
-        <script src='/n2o/template.js'></script>
-        <script src='/n2o/n2o.js'></script>
-        <script src='/n2o/validation.js'></script>
-        <script>protos = [$client,$bert]; N2O_start();</script>
-    </body>
-</html>

+ 55 - 0
apps/n2o_sample/priv/templates/index.dtl

@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link href="/static/synrc.css" rel="stylesheet">
+<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
+<title>Chat</title>
+</head>
+<body>
+<table cellpadding="20" align="center"><!-- todo : upd template -- styles to css, table to css grids -->
+  <tr>
+    <td valign="top" align="center" height="230" width="300">
+      <br><a href="/doc"><img src="/static/S.svg" border="0" height="200" style="margin-top:0px;"></a><br>
+    </td>
+    <td colspan="2" width="700" valign="top">
+      <h1><b id="heading">Roster</b></h1>
+      <h2>Unique Feed<br><button id="logout"></button></h2>
+    </td>
+  </tr>
+  <tr>
+    <td width="300" valign="top">
+      <p>Just type what you think about this:</p>
+      <textarea id="message" style="margin-left:5px; width:490px; font-size:16pt;" value="" rows="5" autofocus></textarea>
+      {{body}}
+      <div id="history"></div>
+    </td>
+    <td colspan="2" width="700" valign="top" height="550" bgcolor="#eeeeee">
+      {{list}}
+    </td>
+  </tr>
+</table>
+
+<!--script>var transition = {pid: '', host: 'localhost', port:'8000'};</script-->
+<script>{{script}}</script>
+{# {{javascript}} #}
+<script src="/js/n2o/protocols/bert.js" defer></script>
+<script src="/js/n2o/protocols/client.js" defer></script>
+<script src="/js/n2o/protocols/nitro.js" defer></script>
+<script src="/js/n2o/bullet.js" defer></script>
+<script src="/js/n2o/xhr.js" defer></script>
+<script src="/js/n2o/utf8.js" defer></script>
+<script src="/js/n2o/template.js" defer></script>
+<script src="/js/n2o/n2o.js" defer></script>
+<script src="/js/n2o/validation.js" defer></script>
+<script src="/js/n2o/ftp.js" defer></script>
+<script>
+window.addEventListener("load", function(){
+  protos = [$client, $bert];
+  N2O_start();
+},false);
+</script>
+
+</body>
+</html>

+ 0 - 37
apps/n2o_sample/priv/templates/index.html

@@ -1,37 +0,0 @@
-<html>
-    <head>
-        <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
-        <link href='/static/synrc.css' type='text/css' rel='stylesheet'>
-        <link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
-        <title>Chat</title>
-    </head>
-    <body>
-        <table cellpadding='20' align='center'>
-            <tr>
-                <td valign='top' align='center' height='230' width='300'>
-                    <br><a href="/doc"><img src='/static/S.svg' border='0' height='200' style='margin-top:0px;'></a><br>
-                </td>
-                <td colspan='2' width='700' valign='top'>
-                    <h1><b id='heading'>Roster</b></h1>
-                    <h2>Unique Feed<br><button id='logout'></button></h2>
-                </td>
-            </tr>
-            <tr>
-                <td width='300' valign='top'>
-                    <p>Just type what you think about this:</p>
-                    <textarea id='message' style='margin-left:5px; width:490px; font-size:16pt;' 
-          value='' rows='5' autofocus></textarea>
-                    {{body}}
-                    <div id='history'></div>
-                </td>
-                <td colspan='2' width='700' valign='top' height='550' bgcolor='#eeeeee'>
-                    {{list}}
-                </td>
-            </tr>
-        </table>
-        <!--script>var transition = {pid: '', host: 'localhost', port:'8000'};</script-->
-        <script>{{script}}</script>
-        {{javascript}}
-        <script>protos = [$bert]; N2O_start();</script>
-    </body>
-</html>

+ 31 - 0
apps/n2o_sample/priv/templates/layout.dtl

@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>{{page_title}}</title>
+{{page_css}}
+</head>
+<body>
+{{page_body}}
+
+<!--script>var transition = {pid: '', host: 'localhost', port:'8000'};</script-->
+<script>{{script}}</script>
+<script src="/js/n2o/protocols/bert.js" defer></script>
+<script src="/js/n2o/protocols/client.js" defer></script>
+<script src="/js/n2o/protocols/nitro.js" defer></script>
+<script src="/js/n2o/bullet.js" defer></script>
+<script src="/js/n2o/xhr.js" defer></script>
+<script src="/js/n2o/utf8.js" defer></script>
+<script src="/js/n2o/template.js" defer></script>
+<script src="/js/n2o/n2o.js" defer></script>
+<script src="/js/n2o/validation.js" defer></script>
+<script>
+window.addEventListener("load", function(){
+  protos = [$client, $bert];
+  N2O_start();
+}, false);
+</script>
+
+{{page_js}}
+</body>
+</html>

+ 56 - 0
apps/n2o_sample/priv/templates/login.dtl

@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=0.5">
+<link href="/static/synrc.css" rel="stylesheet">
+<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
+<title>Login</title>
+</head>
+<body>
+<table cellpadding="20" align="center">
+  <tr>
+    <td valign="top" align="center" height="230" width="300">
+      <a href="/doc"><img src="/static/S.svg" border="0" height="200" style="margin-top:0px;"></a><br>
+    </td>
+    <td colspan="2" width="700" valign="top">
+      <h1><b>N2O</b></h1>
+      <h2>Simple Review Application</h2>
+    </td>
+  </tr>
+  <tr>
+    <td width="300" valign="top">
+      <p>List of spawned feeds:</p>
+      <div id="history" class="feed"></div><br><br>
+      <p>List of inhabited realms:</p>
+      <div class="feed">
+        {{folders}}
+      </div>
+    </td>
+    <td colspan="2" width="700" valign="top" height="550" bgcolor="#eeeeee">
+      <h1>Anyname Login7</h1>
+      {{body}}
+    </td>
+  </tr>
+</table>
+
+<script>{{script}}</script>
+<script src="/js/n2o/protocols/bert.js" defer></script>
+<script src="/js/n2o/protocols/client.js" defer></script>
+<script src="/js/n2o/protocols/nitro.js" defer></script>
+<script src="/js/n2o/bullet.js" defer></script>
+<script src="/js/n2o/xhr.js" defer></script>
+<script src="/js/n2o/utf8.js" defer></script>
+<script src="/js/n2o/template.js" defer></script>
+<script src="/js/n2o/n2o.js" defer></script>
+<script src="/js/n2o/validation.js" defer></script>
+<script src="/js/n2o/ftp.js" defer></script>
+<script>
+window.addEventListener("load", function(){
+  protos = [$client, $bert];
+  N2O_start();
+},false);
+</script>
+
+</body>
+</html>

+ 0 - 47
apps/n2o_sample/priv/templates/login.html

@@ -1,47 +0,0 @@
-<html>
-    <head>
-        <meta name='viewport' content='initial-scale=0.5'>
-        <link href='/static/synrc.css' type='text/css' rel='stylesheet'>
-        <link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
-        <title>Login</title>
-    </head>
-    <body>
-
-        <table cellpadding='20' align='center'>
-            <tr>
-                <td valign='top' align='center' height='230' width='300'>
-                    <a href="/doc"><img src='/static/S.svg' border='0' height='200' style='margin-top:0px;'></a><br>
-                </td>
-                <td colspan='2' width='700' valign='top'>
-                    <h1><b>N2O</b></h1>
-                    <h2>Simple Review Application</h2>
-                </td>
-            </tr>
-            <tr>
-                <td width='300' valign='top'>
-                    <p>List of spawned feeds:</p>
-                    <div id='history' class='feed'></div><br><br>
-                    <p>List of inhabited realms:</p>
-                    <div class='feed'>
-                        {{folders}}
-                    </div>
-                </td>
-                <td colspan='2' width='700' valign='top' height='550' bgcolor='#eeeeee'>
-                    <h1>Anyname Login</h1>
-                    {{body}}
-                </td>
-            </tr>
-        </table>
-        <script>{{script}}</script>
-        <script src='/n2o/protocols/bert.js'></script>
-        <script src='/n2o/protocols/client.js'></script>
-        <script src='/n2o/protocols/nitrogen.js'></script>
-        <script src='/n2o/bullet.js'></script>
-        <script src='/n2o/xhr.js'></script>
-        <script src='/n2o/utf8.js'></script>
-        <script src='/n2o/template.js'></script>
-        <script src='/n2o/n2o.js'></script>
-        <script src='/n2o/validation.js'></script>
-        <script>protos = [$client,$bert]; N2O_start();</script>
-    </body>
-</html>

+ 7 - 0
apps/n2o_sample/priv/templates/message.dtl

@@ -0,0 +1,7 @@
+<table width="500" cellpadding="20" cellspacing="3">
+<tr>
+  <td bgcolor="{{color}}" style="color:white;">
+    <b>{{user}}</b>: {{message}}
+  </td>
+</tr>
+</table>

+ 0 - 3
apps/n2o_sample/priv/templates/message.html

@@ -1,3 +0,0 @@
-<table width="500" cellpadding="20" cellspacing="3">
-<tr><td bgcolor="{{color}}" style='color:white;'><b>{{user}}</b>: {{message}}</td></tr>
-</table>

+ 28 - 0
apps/n2o_sample/priv/templates/null.dtl

@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Sample</title>
+</head>
+<body>
+
+<!--script>var transition = {pid: '', host: 'localhost', port:'8000'};</script-->
+<script>{{script}}</script>
+<script src="/js/n2o/protocols/bert.js" defer></script>
+<script src="/js/n2o/protocols/client.js" defer></script>
+<script src="/js/n2o/protocols/nitro.js" defer></script>
+<script src="/js/n2o/bullet.js" defer></script>
+<script src="/js/n2o/xhr.js" defer></script>
+<script src="/js/n2o/utf8.js" defer></script>
+<script src="/js/n2o/template.js" defer></script>
+<script src="/js/n2o/n2o.js" defer></script>
+<script src="/js/n2o/validation.js" defer></script>
+<script>
+window.addEventListener("load", function(){
+  protos = [$client, $bert];
+  N2O_start();
+}, false);
+</script>
+
+</body>
+</html>

+ 10 - 0
apps/n2o_sample/priv/templates/testcities.dtl

@@ -0,0 +1,10 @@
+<div class="container">
+  <p>{{cities}}:</p>
+  <div id="cities"></div>
+  <p><button id="citiesshow">{{cities_show}}</button></p>
+  <br><br><br>
+  <p>{{cities_add}}:</p>
+  <p><input id="cityname" type="text" placeholder="{{city_name}}"></p>
+  <p><input id="citypop" type="number" min="1" placeholder="{{city_pop}}" value="1"></p>
+  <p><button id="cityadd">{{city_add}}</button></p>
+</div>

+ 5 - 5
apps/n2o_sample/rebar.config

@@ -2,9 +2,9 @@
 {lib_dirs, ["../../apps"]}.
 {deps, []}.
 {erlydtl_opts, [
-    {doc_root,   "priv/templates"},
-    {out_dir,    "ebin"},
-   {compiler_options, [report, return, debug_info]},
-    {source_ext, ".html"},
-    {module_ext, "_view"}
+  {doc_root,   "priv/templates"},
+  {out_dir,    "ebin"},
+  {compiler_options, [report, return, debug_info]},
+  {source_ext, ".dtl"},
+  {module_ext, "_view"}
 ]}.

+ 6 - 1
apps/n2o_sample/src/config.erl

@@ -1,5 +1,10 @@
 -module(config).
--compile(export_all).
+
+-export([
+  log_level/0,
+  log_modules/0
+]).
+
 
 log_level() -> info.
 log_modules() -> % any

+ 0 - 53
apps/n2o_sample/src/doc.erl

@@ -1,53 +0,0 @@
--module(doc).
--compile(export_all).
--include_lib("kvs/include/feed.hrl").
--include_lib("nitro/include/nitro.hrl").
--include_lib("n2o/include/wf.hrl").
-
-main() -> #dtl{file="doc", app=n2o_sample, bindings=[{body, body()}]}.
-
-body() ->
-  case wf:user() of
-    undefined -> wf:user("anonymous");
-    _ -> skip
-  end,
-  [#h2     { body = "Docs search" },
-   #textbox{ id   = query },
-   #button { body = "Search", postback=search, source=[query]},
-   #panel  { id   = results }].
-
-event({client, Panel}) -> wf:insert_top(results, Panel);
-event(search) ->
-  wf:update(results, #panel{id=results}),
-  Pid = self(),
-  Query = wf:q(query),
-  spawn(fun() -> search(Pid, Query) end),
-  ok;
-event(_) -> [].
-
-sections(Path,Match,Pid)  ->
-  Page = filename:basename(Path),
-  App = lists:nth(4, lists:reverse(filename:split(Path))),
-  Forms=#panel{body=[
-    #h5{body=filename:join([App, filename:basename(Page, ".htm")])},
-    [ begin
-      Url = ["index.htm?code=",
-             wf:pickle(iolist_to_binary([wf:to_binary(App), "/doc/web/", Page, $#, Sec])) ],
-      #panel{body = #link{body=T, href=Url}}
-    end || [Sec, T] <- Match]]},
-  Pid ! {client, Forms}.
-
-
-re(Q) -> <<"(?:<h\\d[^>]*?\\ id=[\'\"](sec\\d{1,3})[\'\"][^>]*?>(.*?)<\\/h\\d>.*?)+", Q/binary, "[^>]+<">>.
-
-search(Pid,Q) ->
-  lists:map(fun(Path) ->
-    spawn(fun() ->
-      {ok,Bin} = file:read_file(Path),
-      case re:run(Bin, re(Q), [unicode, global, {capture, [1, 2], binary}, dotall, caseless]) of
-        {match, Match} -> sections(Path, Match, Pid);
-        nomatch -> []
-      end
-    end)
-  end, filelib:wildcard(application:get_env(n2o, search, "/var/www/sites/synrc.com/apps/*/doc/web/*.htm")) ).
-

+ 22 - 0
apps/n2o_sample/src/hg.erl

@@ -0,0 +1,22 @@
+-module(hg).
+
+-compile([export_all, nowarn_export_all]).
+
+%% html generate module
+
+% generate_cities_list(Data)
+% 
+
+generate_cities_list(Data) ->
+  generate_cities_list2(Data, []).
+
+generate_cities_list2([],[]) ->
+  <<"<p>Міст ще немає.</p>"/utf8>>;
+generate_cities_list2([],Acc) ->
+  lists:reverse(Acc);
+generate_cities_list2([{_Id,Name,Pop}|T],Acc) ->
+  RZ = [<<"<p>"/utf8>>, Name, <<" - населення: "/utf8>>, erlang:integer_to_binary(Pop), <<"</p>"/utf8>>],
+  generate_cities_list2(T,[RZ|Acc]);
+generate_cities_list2(_,_) ->
+  <<"<p>Помилка СУБД!</p>"/utf8>>.
+

+ 76 - 0
apps/n2o_sample/src/hm.erl

@@ -0,0 +1,76 @@
+-module(hm).
+
+-compile([export_all, nowarn_export_all]).
+
+%-include_lib("nitro/include/nitro.hrl").
+-include_lib("n4u/include/wf.hrl").
+%-include_lib("kernel/include/file.hrl").
+
+%% help module
+
+%% get params
+
+% get_language_from_header(Req)
+% get_language_from_cookies(Name, Req)
+% get_language(Name, Req)
+% get_tr_mod()
+
+%% todo mv this funcs to framework
+
+%% get params
+
+
+get_language_from_header(Req) ->
+  {Headers,_} = wf:headers(Req),
+  Accept_language = [ V || {K,V} <- Headers, K =:= <<"accept-language">> ], %% todo header to lowercase
+  case Accept_language of
+    [] -> en;
+    [H|_] ->
+      <<Lang:2/binary,_/binary>> = H,
+      case lists:member(Lang, [en, uk]) of %% todo mv to config langs list and default lang
+        false -> en;
+        true -> Lang
+      end
+  end.
+
+
+get_language_from_cookies(Name, Req) ->
+  case wf:cookie_req(Name,Req) of
+    undefined -> false;
+    Lang ->
+      case lists:member(Lang, [en, uk]) of
+        false -> false;
+        true -> Lang
+      end
+  end.
+
+
+get_language(Name, Req) ->
+  case ?MODULE:get_language_from_cookies(Name, Req) of
+    false ->
+      ?MODULE:get_language_from_header(Req);
+    Lang ->
+      Lang
+  end.
+
+
+get_tr_mod() -> %% modules with i18n for config langs list -- en -> tr_en, uk -> tr_uk etc
+  Lang = hm:get_language(<<"lang">>, ?REQ),
+  case Lang of
+    uk -> tr_uk;
+    %en -> tr_en
+    _ -> tr_en
+  end. 
+
+
+post_value(Req, Key) ->
+  {ok, PostStr, _} = wf:request_body(Req),
+  PostList = cow_qs:parse_qs(PostStr),
+  ?MODULE:post_value2( [ Value || {_Key1, Value} <- PostList, _Key1 =:= Key ] ).
+
+% helper
+post_value2([]) -> undefined;
+post_value2([Val|_]) -> Val;
+post_value2(_) -> undefined.
+
+

+ 24 - 15
apps/n2o_sample/src/index.erl

@@ -1,27 +1,33 @@
 -module(index).
--compile(export_all).
+
 -include_lib("kvs/include/entry.hrl").
 -include_lib("nitro/include/nitro.hrl").
--include_lib("n2o/include/wf.hrl").
+-include_lib("n4u/include/wf.hrl").
+
+
+-export([
+  main/0,
+  event/1,
+  loop/1
+]).
+
 
 main() ->
   case wf:user() of
     undefined ->
-      wf:redirect("login.htm"),
-      redirect_wait();
+      wf:redirect("login.html"),
+      [];
     _ ->
-      #dtl{file = "index", app = n2o_sample, bindings = [{body, body()}, {list, list()}, {javascript, (?MODULE:(wf:config(n2o, mode, dev)))()}]}
+      #dtl{file = "index", app = n2o_sample, bindings = [
+        {body, body()},
+        {list, list()}
+      ]}
   end.
 
-prod() ->   [ #script{src="/static/n2o_sample.min.js"} ].
-dev()  -> [ [ #script{src=lists:concat(["/n2o/protocols/",X,".js"])} || X <- [bert, nitrogen] ],
-            [ #script{src=lists:concat(["/n2o/",Y,".js"])}           || Y <- [bullet, n2o, ftp, utf8, validation] ] ].
-
-redirect_wait() -> #dtl{}.
-list() -> "<iframe src=http://synrc.com/apps/" ++ code() ++ " frameborder=0 width=700 height=1250></iframe>".
+list() -> "".
 code() ->
   case wf:q(<<"code">>) of
-    undefined -> "../privacy.htm";
+    undefined -> "../privacy.html";
     Code -> wf:to_list(wf:depickle(Code))
   end.
 
@@ -32,8 +38,7 @@ body() ->
   [ #span{id=upload}, #button{id=send, body= <<"Chat">>, postback=chat, source=[message] } ].
 
 event(init) ->
-  %wf:config(n2o, appurl, undefined) %% App, Key, DefaulValue
-  io:format("~p~n", [wf:config(n2o, appurl, undefined)]),
+  io:format("~p~n", [wf:config(n4u, appurl, undefined)]),
   
   Room = code(),
   wf:update(upload, #upload{id=upload}),
@@ -79,7 +84,11 @@ event(Event) ->
   ok.
 
 loop(M) ->
-  DTL = #dtl{file="message", app=n2o_sample, bindings=[{user, "system"}, {message, M}, {color, "silver"}]},
+  DTL = #dtl{file="message", app=n2o_sample, bindings=[
+    {user, "system"},
+    {message, M},
+    {color, "silver"}
+  ]},
   wf:insert_top(history, wf:jse(wf:render(DTL))),
   wf:flush().
 

+ 0 - 93
apps/n2o_sample/src/interlogin.erl

@@ -1,93 +0,0 @@
--module(interlogin).
--compile(export_all).
--include_lib("nitro/include/nitro.hrl").
--include_lib("n2o/include/wf.hrl").
--include_lib("kvs/include/user.hrl").
--include_lib("avz/include/avz.hrl").
--include_lib("kvs/include/user.hrl").
-
--define(LOGIN, [facebook, twitter, google, github, microsoft]).
-
-main() ->
- avz:callbacks(?LOGIN),
- #dtl{file="dev", app=n2o_sample, bindings=[{title, <<"Login">>}, {body, body()}, {folders, folders()}]}.
-folders() -> string:join([filename:basename(F) || F <- filelib:wildcard(code:priv_dir(n2o_sample) ++ "/snippets/*/")], ",").
-
-body() ->
-  header() ++
-  [#panel{class=main, body=[
-     #panel{class=section, body=[
-       #panel{class=[content,center], body=[
-         #span{ id=display },
-         #link{href="/", body=[#image{src="/static/S.svg", style="margin-top:0px;"}]},
-         #h1{body= <<"Sign In/Up">>},
-         #h2{body= <<"Select provider to enter through">>},
-         #br{},
-         #panel{class=["pure-button-group"], role=group, body=[
-           avz:buttons(?LOGIN)
-         ]}
-       ]}
-     ]},
-     footer()
-  ]} | avz:sdk(?LOGIN)].
-
-event(init) ->
-  [wf:wire(#jq{target=Id, method=["classList.add"], args=["'pure-button','avz-button'"]})
-    || Id <- [loginfb, twlogin, github_btn, microsoftbtn]],
-  wf:wire(#jq{target=gloginbtn, method=["classList.add"], args=["'google-button'"]});
-
-event({register, U=#user{}}) ->
-  case kvs:add(U#user{id=kvs:next_id("user", 1)}) of
-    {ok, U1} -> avz:login_user(U1);
-    {error, E} -> event({login_failed, E})
-  end;
-
-event({login, #user{}=U, N}) ->
-  Updated = avz:merge(U,N),
-  ok = kvs:put(Updated),
-  avz:login_user(Updated);
-
-event({login_failed, E}) ->
-  wf:update(display, #span{id=display, body=[E]});
-
-event(X) ->
-  wf:info(?MODULE, "Event:~p~n", [X]),
-  avz:event(X).
-
-api_event(X,Y,Z) -> avz:api_event(X,Y,Z).
-
-
-header() -> 
-  [#header{class=[header], body=[
-     #panel{class=["menu", "pure-menu", "pure-menu-horizontal", "pure-menu-fixed"], body=[
-       #link{class=["pure-menu-heading"], href= <<"/">>, body=[#image{src="/static/S.svg"}, <<"Home">>]},
-
-       #list{class=["pure-menu-list"], body=[
-         case wf:user() of 
-           undefined -> [
-             #li{class=["pure-menu-item", "pure-menu-selected"], 
-                 body=[#link{class=["pure-menu-link"], body= <<"Sign In">>, url= <<"/login.html">>}]} ];
-           User ->
-             Email = wf:to_list(User#user.email),
-             Avatar = case User#user.images of
-               [] -> "holder.js/50x50";
-               [{_,A}|_] -> A
-             end,
-             [
-              #li{class=["pure-menu-item"], body=[#link{class=["pure-menu-link"], body= [<<"Profile">>], url= <<"/profile.html">>}]},
-              #li{class=["pure-menu-item"], body=[
-                #link{class=["pure-menu-link"], body= ["Sign Out"], postback=logout, delegate=index} ]},
-              #li{class=["pure-menu-item"], body=[Email]},
-              #li{class=["pure-menu-item"], body=[
-                #link{class=["pure-menu-link", "profile-pic"], body=[
-                  #image{class=["pure-img"], image = Avatar} ]}
-              ]}
-             ]
-         end
-       ]}
-     ]}
-  ]}].
-
-
-footer() -> #footer{class=[footer], body=[ <<"&copy; 2017">> ]}.
-

+ 20 - 8
apps/n2o_sample/src/login.erl

@@ -1,17 +1,29 @@
 -module(login).
--compile(export_all).
+
 -include_lib("kvs/include/feed.hrl").
 -include_lib("nitro/include/nitro.hrl").
--include_lib("n2o/include/wf.hrl").
+-include_lib("n4u/include/wf.hrl").
+
+
+-export([
+  main/0,
+  event/1
+]).
+
+
+main() ->
+  #dtl{file = "login", app = n2o_sample, bindings = [
+    {body, body()},
+    {folders, folders()}
+  ]}.
 
-main() -> #dtl{file="login", app=n2o_sample, bindings=[{body, body()}, {folders, folders()}]}.
 folders() -> string:join([filename:basename(F) || F <- filelib:wildcard(code:priv_dir(n2o_sample) ++ "/snippets/*/")], ",").
 
 body() ->
- [#span  { id=display },                 #br{},
-  #span  { body="Login: " },             #textbox{id=user, autofocus=true}, #br{},
-  #span  { body="Join/Create Feed: " },  #textbox{id=pass},
-  #button{ id=loginButton, body="Login", postback=login, source=[user, pass]} ].
+ [#span  { id = "display" },               #br{},
+  #span  { body = "Login: " },             #textbox{id = "user", autofocus = true}, #br{},
+  #span  { body = "Join/Create Feed: " },  #textbox{id = "pass"},
+  #button{ id = "loginButton", body = "Login", postback = login, source=[user, pass]} ].
 
 event(login) ->
   User = case wf:q(user) of
@@ -21,7 +33,7 @@ event(login) ->
   end,
   wf:user(User),
   wf:info(?MODULE, "User: ~p", [wf:user()]),
-  wf:redirect("index.htm?room=" ++ wf:to_list(wf:q(pass))),
+  wf:redirect("index.html?room=" ++ wf:to_list(wf:q(pass))),
   ok;
 
 event(_) -> [].

+ 0 - 9
apps/n2o_sample/src/n2o_sample.app.src

@@ -1,9 +0,0 @@
-{application, n2o_sample,
- [
-  {description, "N2O Simple Sample Application"},
-  {vsn, "2.9"},
-  {registered, []},
-  {applications, [kernel, stdlib, n2o, kvs]},
-  {mod, { n2o_sample, []}},
-  {env, []}
- ]}.

+ 0 - 37
apps/n2o_sample/src/n2o_sample.erl

@@ -1,37 +0,0 @@
--module(n2o_sample).
--behaviour(supervisor).
--behaviour(application).
--export([init/1, start/2, stop/1, main/1]).
--include_lib("kvs/include/user.hrl").
-
-main(A)    -> mad:main(A).
-%start()    -> start(normal,[]).
-start(_,_) -> supervisor:start_link({local, n2o_sample}, n2o_sample,[]).
-stop(_)    -> ok.
-
--define(USERS, [#user{id="maxim",  email="maxim@synrc.com"},
-                #user{id="doxtop", email="doxtop@synrc.com"},
-                #user{id="roman",  email="roman@github.com"}]).
-
-init([]) ->
-  users:init(),
-  users:populate(?USERS),
-  kvs:join(),
-  {ok, {{one_for_one, 5, 10}, [spec()]}}.
-
-spec()   -> ranch:child_spec(http, 100, ranch_tcp, port(), cowboy_protocol, env()).
-env()    -> [ { env, [ { dispatch, points() } ] } ].
-static() ->   { dir, "apps/n2o_sample/priv/static", mime() }.
-n2o()    ->   { dir, "deps/n2o/priv",           mime() }.
-mime()   -> [ { mimetypes, cow_mimetypes, all   } ].
-port()   -> [ { port, wf:config(n2o, port, 8000)  } ].
-points() -> cowboy_router:compile([{'_', [
-    {"/static/[...]",       n2o_static,    static()},
-    {"/n2o/[...]",          n2o_static,    n2o()},
-    {"/multipart/[...]",    n2o_multipart, []},
-    {"/rest/:resource",     rest_cowboy,   []},
-    {"/rest/:resource/:id", rest_cowboy,   []},
-    {"/ws/[...]",           n2o_stream,    []},
-    {'_',                   n2o_cowboy,    []}
-  ]}]).
-

+ 24 - 0
apps/n2o_sample/src/n2o_sample_app.erl

@@ -0,0 +1,24 @@
+-module(n2o_sample_app).
+
+-behaviour(application).
+
+-export([
+  start/2,
+  stop/1
+%  main/1
+]).
+
+
+%main(A)    -> mad:main(A).
+%start()    -> start(normal,[]).
+start(_,_) ->
+  %%[application:start(A) || A <- [asn1,kernel,stdlib,fs,ranch,compiler,syntax_tools,mnesia,crypto,inets,kvs,cowlib,public_key,ssl,cowboy,n4u,n4u_sample,active,avz,epgsql,erlydtl,gproc,jsone,mad,nitro,oauth,rest,sh] ],
+  [application:start(A) || A <- [asn1,kernel,stdlib,fs,ranch,compiler,syntax_tools,mnesia,crypto,inets,kvs,cowlib,public_key,ssl,cowboy,n4u,active,avz,epgsql,erlydtl,gproc,jsone,mad,nitro,oauth,rest,sh] ],
+  
+  {ok, _} = application:ensure_all_started(ranch),
+  {ok, _} = application:ensure_all_started(cowboy),
+  
+  n2o_sample_sup:start_link().
+
+stop(_)    -> ok.
+

+ 49 - 0
apps/n2o_sample/src/n2o_sample_sup.erl

@@ -0,0 +1,49 @@
+-module(n2o_sample_sup).
+
+-behaviour(supervisor).
+
+-include_lib("kvs/include/user.hrl").
+
+
+-export([
+  start_link/0,
+  init/1
+]).
+
+
+start_link() ->
+  supervisor:start_link({local, ?MODULE}, ?MODULE,[]).
+
+
+-define(USERS, [ #user{id = "maxim",  email = "maxim@synrc.com"},
+                 #user{id = "doxtop", email = "doxtop@synrc.com"},
+                 #user{id = "roman",  email = "roman@github.com"} ]).
+
+
+init([]) ->
+  users:init(),
+  users:populate(?USERS),
+  kvs:join(),
+  {ok, {{one_for_one, 10, 10}, [spec()]}}.
+
+
+spec()   -> ranch:child_spec(http, 100, ranch_tcp, port(), cowboy_protocol, env()).
+env()    -> [ { env, [ { dispatch, points() } ] } ].
+static() ->   { dir, "apps/n2o_sample/priv/static", mime() }.
+static2() ->  { dir, "apps/n2o_sample/priv/js", mime() }.
+%n4u()    ->   { dir, "deps/n4u/priv",           mime() }.
+mime()   -> [ { mimetypes, cow_mimetypes, all   } ].
+port()   -> [ { port, wf:config(n4u, port, 8000)  } ].
+points() -> cowboy_router:compile([{'_', [
+    {"/static/[...]",       n2o_static,    static()}, %% static without nginx by cowboy  % todo : clean - mv to stc folder
+    {"/js/[...]",           n2o_static,   static2()}, %% static without nginx by cowboy
+    {"/css/[...]",          n2o_static,    static()}, %% static without nginx by cowboy
+    {"/img/[...]",          n2o_static,    static()}, %% static without nginx by cowboy
+%    {"/n4u/[...]",          n2o_static,    n4u()},    %% static without nginx by cowboy
+    {"/multipart/[...]",    n2o_multipart, []},
+    {"/rest/:resource",     rest_cowboy,   []},
+    {"/rest/:resource/:id", rest_cowboy,   []},
+    {"/ws/[...]",           n2o_stream,    []},
+    {'_',                   n2o_cowboy,    []}
+  ]}]).
+

+ 9 - 0
apps/n2o_sample/src/n4u_sample.app.src

@@ -0,0 +1,9 @@
+{application, n4u_sample,
+ [
+  {description, "N4U (N2O v4.4 fork) Simple Sample Application"},
+  {vsn, "4.4"},
+  {registered, []},
+  {applications, [kernel, stdlib, n4u, kvs]},
+  {mod, { n2o_sample_app, []}},
+  {env, []}
+ ]}.

+ 79 - 0
apps/n2o_sample/src/pgm.erl

@@ -0,0 +1,79 @@
+-module(pgm).
+
+%% postgresql conn and queries wrappers module
+
+
+-export([
+  mypg/0,
+  transaction/2,
+  select/3,
+  in_up_del/3,
+  returning/3
+]).
+
+
+mypg() ->
+  %{ok, C} = epgsql:connect("localhost", "user", "pass", [{database, "test"}, {port, 5432}, {timeout, 2000}]), %% can be used without connection pool
+  Conn = epgsql:connect(
+    application:get_env(n4u, pgs_host, "localhost"), %% can be used without connection pool
+    application:get_env(n4u, pgs_user, "user"),      %% gets settings from sys.config
+    application:get_env(n4u, pgs_pass, "pass"),
+    [{database, application:get_env(n4u, pgs_db, "test")},
+     {port, application:get_env(n4u, pgs_port, 6432)},
+     {timeout, 3000}]),
+  case Conn of
+    {ok, C} ->
+      C;
+    {error, M} ->
+      io:format("Database error: ~p~n", [epgsql_errcodes:to_name(M)]),
+      wf:wire(["alert('db conn error!');"]),
+      %{error, M}
+      error
+  end.
+
+
+transaction(Ppid, Fun) ->
+  case epgsql:squery(Ppid, "BEGIN") of
+    {ok, _, _} ->
+      try
+        Result = Fun(),
+        epgsql:squery(Ppid, "COMMIT"),
+        Result
+      catch
+        Err1:Err2 ->
+          epgsql:squery(Ppid, "ROLLBACK"),
+          {Err1, Err2}
+      end;
+    Error -> Error
+  end.
+
+
+select(Ppid, Q, A) ->
+  case epgsql:equery(Ppid, Q, A) of
+    {ok, _, R} ->
+      R;
+    {error, E} ->
+      io:format("~p~n", [E]),
+      {error, E}
+  end.
+
+
+in_up_del(Ppid, Q, A) ->
+  case epgsql:equery(Ppid, Q, A) of
+    {ok, R} ->
+      R;
+    {error, E} ->
+      io:format("~p~n", [E]),
+      {error, E}
+  end.
+
+
+returning(Ppid, Q, A) ->
+  case epgsql:equery(Ppid, Q, A) of
+    {ok, 1, _, R} ->
+      R;
+    {error, E} ->
+      io:format("~p~n", [E]),
+      {error, E}
+  end.
+

+ 33 - 0
apps/n2o_sample/src/pq.erl

@@ -0,0 +1,33 @@
+-module(pq).
+
+-compile([export_all, nowarn_export_all]).
+
+%% postgresql queries module
+
+% get_all_cities(Mpid)
+% update_city_by_id(Mpid, City_Id, City_Name, City_Pop)
+% add_city(Mpid, City_Name, City_Pop)
+% add_city_return_id(Mpid, City_Name, City_Pop)
+% delete_city_by_id(Mpid, City_Id)
+% 
+
+
+get_all_cities(Ppid) ->
+  pgm:select(Ppid, "SELECT id, name, population FROM test ORDER BY id", []).
+
+
+update_city_by_id(Ppid, City_Id, City_Name, City_Pop) ->
+  pgm:in_up_del(Ppid, "UPDATE test SET name = $1, population = $2 WHERE id = $3", [City_Name, City_Pop, City_Id]).
+
+
+add_city(Ppid, City_Name, City_Pop) ->
+  pgm:in_up_del(Ppid, "INSERT INTO test (name, population) VALUES ($1, $2)", [City_Name, City_Pop]).
+
+
+add_city_return_id(Ppid, City_Name, City_Pop) ->
+  pgm:returning(Ppid, "INSERT INTO test (name, population) VALUES ($1, $2) RETURNING id", [City_Name, City_Pop]).
+
+
+delete_city_by_id(Ppid, City_Id) ->
+  pgm:in_up_del(Ppid, "DELETE FROM test WHERE id = $1", [City_Id]).
+

+ 7 - 6
apps/n2o_sample/src/routes.erl

@@ -1,8 +1,11 @@
 -module(routes).
--author('Maxim Sokhatsky').
--include_lib("n2o/include/wf.hrl").
+%%-author('Maxim Sokhatsky').
+
+-include_lib("n4u/include/wf.hrl").
+
 -export([init/2, finish/2]).
 
+
 %% U can use default dynamic routes or define custom static as this
 %% Just put needed module name to sys.config:
 %% {n2o, [{route,routes}]}
@@ -20,12 +23,10 @@ route_prefix(<<"/",P/binary>>) -> route(P);
 route_prefix(P) -> route(P).
 
 route(<<>>)              -> login;
-route(<<"counter",_/binary>>) -> counter;
-route(<<"chat",_/binary>>) -> chat;
-route(<<"doc",_/binary>>) -> doc;
+route(<<"testcities/">>) -> testcities;
 route(<<"index",_/binary>>) -> index;
 route(<<"static/spa/index",_/binary>>) -> index;
 route(<<"static/spa/login",_/binary>>) -> login;
 route(<<"login",_/binary>>) -> login;
-route(<<"interlogin",_/binary>>) -> interlogin;
 route(_) -> login.
+

+ 67 - 0
apps/n2o_sample/src/testcities.erl

@@ -0,0 +1,67 @@
+-module(testcities).
+
+-include_lib("nitro/include/nitro.hrl").
+-include_lib("n4u/include/wf.hrl").
+
+
+-export([
+  main/0,
+  event/1
+]).
+
+
+main() ->
+  Tr = hm:get_tr_mod(), % get i18n module by lang
+  
+  Body = #dtl{file = "testcities", app = n2o_sample, bindings=[
+    {cities, Tr:tr(testcities, cities) },
+    {cities_show, Tr:tr(testcities, cities_show) },
+    {cities_add, Tr:tr(testcities, cities_add) },
+    {city_name, Tr:tr(testcities, city_name) },
+    {city_pop, Tr:tr(testcities, city_pop) },
+    {city_add, Tr:tr(testcities, city_add) }
+  ]},
+
+  #dtl{file = "layout", app = n2o_sample, bindings=[
+    {page_title, Tr:tr(testcities, page_title) },
+    {page_css, <<"<link rel=\"stylesheet\" href=\"/css/test.css\">">>},
+    {page_body, wf:render(Body)},
+    {page_js, <<"<script src=\"/js/testcities.js\" defer></script>">>}
+  ]}.
+
+
+event(init) ->
+  ?MODULE:event({client, {sitiesshow}});
+  %[];
+
+
+event({client, {sitiesshow}}) ->
+  Ppid = pgm:mypg(),
+  Data = pq:get_all_cities(Ppid),
+  epgsql:close(Ppid),
+
+  InnerHtml = hg:generate_cities_list(Data),
+
+  wf:wire(wf:f("var parent = qi('cities');"
+          "parent.innerHTML='~s';"
+          "window.getting_data=false;"
+          "qi('citiesshow').disabled=false;", [unicode:characters_to_binary(InnerHtml, utf8)]));
+
+
+event({client, {cityadd, Name, Pop}}) ->
+  %io:format("~p~n~p~n",[Name, Pop]),
+  Mpid = pgm:mypg(),
+  1 = pq:add_city(Mpid, Name, Pop),
+  epgsql:close(Mpid),
+
+  wf:wire(wf:f("qi('cityname').value='';"
+          "qi('citypop').value='1';"
+          "window.sending_data=false;"
+          "qi('cityadd').disabled=false;", [])),
+  ?MODULE:event({client, {sitiesshow}});
+
+
+event(Event) ->
+  wf:info(?MODULE, "Event: ~p", [Event]),
+  ok.
+

+ 17 - 0
apps/n2o_sample/src/tr_en.erl

@@ -0,0 +1,17 @@
+-module(tr_en).
+
+-export([
+  tr/2
+]).
+
+
+%% tr(Page, Key) ->
+tr(testcities, cities) -> <<"Cities">>;
+tr(testcities, cities_show) -> <<"Reload cities list">>;
+tr(testcities, cities_add) -> <<"Add city to list">>;
+tr(testcities, city_name) -> <<"City name">>;
+tr(testcities, city_pop) -> <<"City population">>;
+tr(testcities, city_add) -> <<"Add city">>;
+
+tr(testcities, page_title) -> <<"Test - cities">>.
+

+ 17 - 0
apps/n2o_sample/src/tr_uk.erl

@@ -0,0 +1,17 @@
+-module(tr_uk).
+
+-export([
+  tr/2
+]).
+
+
+%% tr(Page, Key) ->
+tr(testcities, cities) -> <<"Міста"/utf8>>; %% do not forget /utf8 for cyrillic
+tr(testcities, cities_show) -> <<"Оновити список міст"/utf8>>;
+tr(testcities, cities_add) -> <<"Додати місто в список"/utf8>>;
+tr(testcities, city_name) -> <<"Назва міста"/utf8>>;
+tr(testcities, city_pop) -> <<"Населення міста"/utf8>>;
+tr(testcities, city_add) -> <<"Додати місто"/utf8>>;
+
+tr(testcities, page_title) -> <<"Тест - міста"/utf8>>.
+

+ 5 - 0
apps/n2o_sample/src/users.erl

@@ -1,8 +1,12 @@
 -module(users).
+
 -behaviour(rest).
 -compile({parse_transform, rest}).
+
 -include_lib("kvs/include/user.hrl").
+
 -export([init/0, populate/1, exists/1, get/0, get/1, post/1, delete/1]).
+
 -rest_record(user).
 
 init()               -> ets:new(users, [public, named_table, {keypos, #user.id}]).
@@ -13,3 +17,4 @@ get(Id)              -> #user{id=Id}.
 delete(Id)           -> ets:delete(users, wf:to_list(Id)).
 post(#user{} = User) -> ets:insert(users, User), true;
 post(Data)           -> post(from_json(Data, #user{})), true.
+

+ 12 - 0
apps/n2o_sample/test_pg.sql

@@ -0,0 +1,12 @@
+
+DROP TABLE IF EXISTS test;
+CREATE TABLE "test" (
+  "id" serial,
+  "name" varchar(255) NOT NULL,
+  "population" integer,
+  PRIMARY KEY ("id")
+);
+INSERT INTO "test" ("name", "population") VALUES ('Київ', 2804000);
+INSERT INTO "test" ("name", "population") VALUES ('Львів', 723605);
+INSERT INTO "test" ("name", "population") VALUES ('Одеса', 997189);
+

BIN
mad


+ 97 - 0
note.txt

@@ -0,0 +1,97 @@
+
+2.10.1-erl19
+a1e78d2347f5a72855bd616eef6cf8dffcf6df15
+
+4.4-erl19
+f3fc6f269b8ee2a0643812f7d933ce5826d371ad
+
+
+4.4.1-erl19
+96c4b1320f068cdd07aec004dcc99d14a6bcd0e4
+
+3.9.2-erl19
+c8cc18db06ed7635c8d6b18130721277c586bfae
+
+
+
+{pgs_host, "195.201.41.112"}, %{pgs_host, "localhost"},
+{pgs_port, 5432}, %{pgs_port, 6432},
+{pgs_user, "elixir"}, %{pgs_user, "user33"},
+{pgs_pass, "C9TktOkCLpJqrDFb777Hnf"}, %{pgs_pass, "strong_password_here"}, % 
+{pgs_db, "rich"},%{pgs_db, "test"},
+
+
+
+
+
+
+Path:
+"/home/e/git/n4u_sample/apps/n2o_sample/priv/templates/login.dtl"
+Flags:
+renamed
+[{mad_compile,compile,
+              ["n2o_sample",
+               "/home/e/git/n4u_sample/apps/n2o_sample/priv/templates/login.dtl"],
+              []},
+ {active,compile,3,
+         [{file,"/home/e/git/n4u_sample/deps/active/src/active.erl"},
+          {line,165}]},
+ {active,handle_info,2,
+         [{file,"/home/e/git/n4u_sample/deps/active/src/active.erl"},
+          {line,61}]},
+ {gen_server,try_dispatch,4,[{file,"gen_server.erl"},{line,601}]},
+ {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,667}]},
+ {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]Catch: error:undefPath:
+"/home/e/git/n4u_sample/apps/n2o_sample/priv/templates/login.dtl
+
+
+
+
+==> "erlydtl"
+
+*** Conflicts resolved by operator precedences:
+
+Parse action conflict scanning symbol and_keyword in state 182:
+   Reduce to Unot from not_keyword IfExpression (rule 117 at line 385)
+      vs.
+   shift to state 183, adding right sisters to IfExpression.
+Resolved in favor of reduce.
+
+Parse action conflict scanning symbol or_keyword in state 182:
+   Reduce to Unot from not_keyword IfExpression (rule 117 at line 385)
+      vs.
+   shift to state 184, adding right sisters to IfExpression.
+Resolved in favor of reduce.
+
+Parse action conflict scanning symbol and_keyword in state 185:
+   Reduce to IfExpression from IfExpression or_keyword IfExpression (rule 114 at line 381)
+      vs.
+   shift to state 183, adding right sisters to IfExpression.
+Resolved in favor of shift.
+
+Parse action conflict scanning symbol or_keyword in state 185:
+   Reduce to IfExpression from IfExpression or_keyword IfExpression (rule 114 at line 381)
+      vs.
+   shift to state 184, adding right sisters to IfExpression.
+Resolved in favor of reduce.
+
+Parse action conflict scanning symbol and_keyword in state 186:
+   Reduce to IfExpression from IfExpression and_keyword IfExpression (rule 115 at line 382)
+      vs.
+   shift to state 183, adding right sisters to IfExpression.
+Resolved in favor of reduce.
+
+Parse action conflict scanning symbol or_keyword in state 186:
+   Reduce to IfExpression from IfExpression and_keyword IfExpression (rule 115 at line 382)
+      vs.
+   shift to state 184, adding right sisters to IfExpression.
+Resolved in favor of reduce.
+
+*** End of resolved conflicts
+
+Compiling /deps/erlydtl/src/erlydtl_parser.erl
+
+O_o
+
+
+

+ 16 - 35
otp.mk

@@ -6,7 +6,6 @@ endif
 
 VM       := vm.args
 SYS      := sys.config
-PLT_NAME := ~/.n2o_dialyzer.plt
 ERL_ARGS := -args_file $(VM) -config $(SYS)
 RUN_DIR  ?= rels/web/devbox
 LOG_DIR  ?= rels/web/devbox/logs
@@ -14,48 +13,30 @@ empty    :=
 ROOTS    := apps deps
 space    := $(empty) $(empty)
 comma    := $(empty),$(empty)
-VSN      := $(shell git rev-parse HEAD | head -c 6)
-DATE     := $(shell date "+%Y%m%d-%H%M%S")
 ERL_LIBS := $(subst $(space),$(SEPARATOR),$(ROOTS))
-relx     := "{release,{$(RELEASE),\"$(VER)\"},[$(RELEASE)]}.\\n{include_erts,true}.\
-\\n{extended_start_script,true}.\\n{generate_start_script,true}.\\n{sys_config,\"$(SYS)\"}.\
-\\n{vm_args,\"$(VM)\"}.\\n{overlay,[{mkdir,\"log/sasl\"}]}."
 
-test: eunit ct
-delete-deps get-deps update-deps:
-	rebar $@
+
+#clean:
+#	rm -f applist
+#.applist:
+#	$(eval APPS := $(subst deps/,,$(subst apps/,,$(shell find apps deps -maxdepth 1 -mindepth 1 -type d))))
 compile:
-	rebar compile skip_deps=true
-clean:
-	rm -f .applist
-	rebar $@
-.applist:
-	$(eval APPS := $(subst deps/,,$(subst apps/,,$(shell find apps deps -maxdepth 1 -mindepth 1 -type d))))
-	mad plan
+	./mad dep com
 $(RUN_DIR) $(LOG_DIR):
 	mkdir -p $(RUN_DIR) & mkdir -p $(LOG_DIR)
-console: .applist
-	ERL_LIBS="$(ERL_LIBS)" erl +pc unicode $(ERL_ARGS) -eval '[application:start(A) || A <- $(shell cat .applist)]'
-start: $(RUN_DIR) $(LOG_DIR) .applist
+#console: applist
+console:
+	ERL_LIBS="$(ERL_LIBS)" erl +pc unicode $(ERL_ARGS) -eval 'application:ensure_all_started(n4u_sample)'
+#	ERL_LIBS="$(ERL_LIBS)" erl +pc unicode $(ERL_ARGS) -eval 'application:ensure_all_started($(PROJECT)),n4u_sample:start([],[]),application:start($(PROJECT))'
+#	ERL_LIBS="$(ERL_LIBS)" erl +pc unicode $(ERL_ARGS) -eval 'n4u_sample:start([],[])'
+#	ERL_LIBS="$(ERL_LIBS)" erl +pc unicode $(ERL_ARGS) -eval 'application:start($(PROJECT))'
+#start: $(RUN_DIR) $(LOG_DIR) .applist
+start: $(RUN_DIR) $(LOG_DIR)
 	RUN_ERL_LOG_GENERATIONS=1000 RUN_ERL_LOG_MAXSIZE=20000000 \
 	ERL_LIBS=$(ERL_LIBS) run_erl -daemon $(RUN_DIR)/ $(LOG_DIR)/ "exec $(MAKE) console"
 attach:
 	to_erl $(RUN_DIR)/
-release:
-	echo $(relx) > relx.config && relx
 stop:
-	@kill -9 $(shell ps ax -o pid= -o command=|grep $(RELEASE)|grep $(COOKIE)|awk '{print $$1}')
-$(PLT_NAME):
-	$(eval APPS := $(subst deps/,,$(subst apps/,,$(shell find apps deps -maxdepth 1 -mindepth 1 -type d))))
-	ERL_LIBS=$(ERL_LIBS) dialyzer --build_plt --output_plt $(PLT_NAME) --apps $(APPS) || true
-dialyze: $(PLT_NAME) compile
-	$(eval APPS := $(shell find apps deps -maxdepth 1 -mindepth 1 -type d))
-	@$(foreach var,$(APPS),(echo "Process $(var)"; dialyzer -q $(var)/ebin --plt $(PLT_NAME) --no_native -Werror_handling -Wunderspecs -Wrace_conditions -Wno_undefined_callbacks);)
-tar: release
-	tar zcvf $(RELEASE)-$(VSN)-$(DATE).tar.gz _rel/lib/*/ebin _rel/lib/*/priv _rel/bin _rel/releases
-eunit:
-	rebar eunit skip_deps=true
-ct:
-	rebar ct skip_deps=true verbose=1
+	@kill -9 $(shell ps ax -o pid= -o command=|grep $(PROJECT)|grep $(COOKIE)|awk '{print $$1}')
 
-.PHONY: delete-deps get-deps compile clean console start attach release update-deps dialyze ct eunit tar
+.PHONY: console start attach

+ 19 - 11
rebar.config

@@ -2,33 +2,41 @@
 {lib_dirs,["apps","deps"]}.
 {deps_dir,"deps"}.
 {deps, [
-    {gproc,   ".*", {git, "https://git.4dev.win/n4u/gproc.git",        {tag, "0.6.1"}}},
+    {gproc,   ".*", {git, "https://git.4dev.win/n4u/gproc.git",        {tag, "0.6.1-erl19"}}},
     %{syn,     ".*", {git, "https://git.4dev.win/n4u/syn.git",          {tag, "1.1.0"}}},
     
     {jsone,   ".*", {git, "https://git.4dev.win/n4u/jsone.git",        {tag, "1.4.7"} }},
-    {erlydtl, ".*", {git, "https://git.4dev.win/n4u/erlydtl.git",      {tag, "0.12.1"}}},
-    {nitro,   ".*", {git, "https://git.4dev.win/n4u/nitro.git",        {tag, "2.4-erl19"}  }},
+    {erlydtl, ".*", {git, "https://git.4dev.win/n4u/erlydtl.git",      {tag, "0.12.1-erl19"}}},
     
-    {fs,      ".*", {git, "https://git.4dev.win/n4u/fs.git",           {tag, "1.9"}  }},
-    {active,  ".*", {git, "https://git.4dev.win/n4u/active.git",       {tag, "1.9"}  }},
+    %%{nitro,   ".*", {git, "https://git.4dev.win/n4u/nitro.git",        {tag, "2.4-erl19"}  }},
+    {nitro,   ".*", {git, "https://git.4dev.win/n4u/nitro.git",        {tag, "6.6.4-erl19"}  }},
+    %{nitro,   ".*", {git, "https://git.4dev.win/n4u/nitro.git",        {branch, "n4u"}  }},
+    
+    %{fs,      ".*", {git, "https://git.4dev.win/n4u/fs.git",           {tag, "1.9"}  }},
+    %{active,  ".*", {git, "https://git.4dev.win/n4u/active.git",       {tag, "1.9"}  }},
+    {fs,      ".*", {git, "https://git.4dev.win/n4u/fs.git",           {tag, "6.12"}  }},
+    {active,  ".*", {git, "https://git.4dev.win/n4u/active.git",       {tag, "6.2-erl19"}  }},  %% todo fix hot reload with active and mad
     
     {sh,      ".*", {git, "https://git.4dev.win/n4u/sh.git",           {tag, "1.9-erl19"}  }},
     
+    {epgsql,  ".*", {git, "https://git.4dev.win/n4u/epgsql.git",       {tag, "3.3.0"} }},
+    
     {cowlib,  ".*", {git, "https://git.4dev.win/n4u/cowlib.git",       {branch, "1.0.0-erl19"} }},
     {ranch,   ".*", {git, "https://git.4dev.win/n4u/ranch.git",        {tag, "1.0.0"} }},
-    {cowboy,  ".*", {git, "https://git.4dev.win/n4u/cowboy.git",       {tag, "1.0.1"} }},
+    {cowboy,  ".*", {git, "https://git.4dev.win/n4u/cowboy.git",       {tag, "1.0.1-erl19"} }},
     
-    {n2o,     ".*", {git, "https://git.4dev.win/n4u/n4u.git",          {tag, "4.4"}  }}, 
+    %{n4u,     ".*", {git, "https://git.4dev.win/n4u/n4u.git",        {branch, "master"}  }},
+    {n4u,     ".*", {git, "https://git.4dev.win/n4u/n4u.git",          {tag, "4.4.1-erl19"}  }},
     
-    {rest,    ".*", {git, "https://git.4dev.win/n4u/rest.git",         {tag, "2.9"}  }},
+    {rest,    ".*", {git, "https://git.4dev.win/n4u/rest.git",         {tag, "2.9-erl19"}  }},
     
-    {mad,     ".*", {git, "https://git.4dev.win/n4u/mad.git",          {tag, "1.9-erl19"}  }},
+    %{mad,     ".*", {git, "https://git.4dev.win/n4u/mad.git",          {branch, "n4u"}  }},
+    {mad,     ".*", {git, "https://git.4dev.win/n4u/mad.git",          {tag, "2.10.1-erl19"}  }},
     
-    {kvs,     ".*", {git, "https://git.4dev.win/n4u/kvs.git",          {tag, "3.9.1-erl19"}}},
+    {kvs,     ".*", {git, "https://git.4dev.win/n4u/kvs.git",          {tag, "3.9.2-erl19"}}},
     
     {oauth,   ".*", {git, "https://git.4dev.win/n4u/erlang-oauth.git", {tag, "v1.6.0"}  }},
     {avz,     ".*", {git, "https://git.4dev.win/n4u/avz.git",          {tag, "4.4-erl19"}  }}
-    
 ]}.
 {fetch_speed,fast_master}.
 {verbose,1}.

+ 35 - 27
sys.config

@@ -1,32 +1,39 @@
 [
- {n2o, [{port,4000},
-        %{websocket_port,443},
-        {websocket_port,4000}, % same as port for localhost test
-        {app,n2o_sample},
+ {n4u, [{port, 4000},
+        %{websocket_port, 443}, % uncomment at server behind nginx
+        {websocket_port, 4000}, % same as port for localhost test
+        {app, n4u_sample},
         {appurl, <<"https://mybesttestapp.win/">>},
-        {upload,"./apps/n2o_sample/priv/static/"},
-        {search,"/Users/5HT/depot/synrc/synrc.com/apps/*/doc/web/*.htm"},
-        {mode,dev},
-        {route,routes},
-        {mq,n2o_mq},
-        {formatter,bert},
-        {minify,{"apps/n2o_sample/priv/static",
-                ["deps/n2o/priv/bullet.js",
-                 "deps/n2o/priv/n2o.js",
-                 "deps/n2o/priv/ftp.js",
-                 "deps/n2o/priv/protocols/bert.js",
-                 "deps/n2o/priv/protocols/nitrogen.js",
-                 "deps/n2o/priv/utf8.js",
-                 "deps/n2o/priv/validation.js"]}},
-        {log_modules,config},
-        {log_level,config},
-        {log_backend,n2o_log},
-        {session,n2o_session},
-        {origin,<<"*">>},
-        {bridge,n2o_cowboy},
-        {pickler,n2o_pickle},
-        {erroring,n2o_error},
-        {event,pickle}]},
+        {upload, "./apps/n2o_sample/priv/static/"},
+        {mode, dev},
+        
+        {pgs_host, "localhost"},
+        {pgs_port, 5432},
+        {pgs_user, "user33"},
+        {pgs_pass, "strong_password_here"},
+        {pgs_db, "test"},
+        
+        {route, routes},
+        {mq, n2o_mq},
+        {formatter, bert},
+        {log_modules, config},
+        {log_level, config},
+        {log_backend, n2o_log},
+        {session, n2o_session},
+        {origin, <<"*">>},
+        {bridge, n2o_cowboy},
+        {pickler, n2o_pickle},
+        {erroring, n2o_error},
+        {event, pickle}]},
+ {fs,
+   [{backwards_compatible, false}]},
+ {active,
+   [{compile_on_templates, true},
+    {listen_paths, ["apps", "deps"]}]},
+%{mad,
+%   [{compile_dtl_escape, true}, %% auto_escape dtl templates data -- default enabled in dtl, default disabled in mad
+%    {debug, true}, %% add debug_info when compile erlang modules with mad, default disabled in mad
+%    {warnings_as_errors, true}]}, %% add warnings_as_errors when compile erlang modules with mad, default disabled in mad
  {avz, [{after_login_page, "/index"},
         {login_page, "/interlogin"},
         {fb_id, "176025532423202"},
@@ -47,3 +54,4 @@
  {kvs, [{dba,store_mnesia},
         {schema, [kvs_user, kvs_acl, kvs_feed, kvs_subscription ]} ]}
 ].
+

+ 12 - 0
test_pg.sql

@@ -0,0 +1,12 @@
+
+DROP TABLE IF EXISTS test;
+CREATE TABLE "test" (
+  "id" serial,
+  "name" varchar(255) NOT NULL,
+  "population" integer,
+  PRIMARY KEY ("id")
+);
+INSERT INTO "test" ("name", "population") VALUES ('Київ', 2804000);
+INSERT INTO "test" ("name", "population") VALUES ('Львів', 723605);
+INSERT INTO "test" ("name", "population") VALUES ('Одеса', 997189);
+

+ 2 - 2
vm.args

@@ -1,5 +1,5 @@
--name n2o_sample@127.0.0.1
--setcookie node_runner
+-name n4u_sample@127.0.0.1
+-setcookie fMrcvPyVXCmxRwF273kzJpEuUT4KhBaW
 +pc unicode
 +K true
 +A 5