diff --git a/gun.js b/gun.js index c0f42f12..52e8d3ac 100644 --- a/gun.js +++ b/gun.js @@ -1289,7 +1289,7 @@ if(tmp = cat.soul){ return cb(tmp, as, cat), gun } if(tmp = cat.link){ return cb(tmp, as, cat), gun } gun.get(function(msg, ev){ - if(u === msg.put && (tmp = (obj_map(cat.root.opt.peers, function(v,k,t){t(k)})||[]).length) && acks++ <= tmp){ + if(u === msg.put && (tmp = (obj_map(cat.root.opt.peers, function(v,k,t){t(k)})||[]).length) && ++acks < tmp){ return; } ev.rid(msg); diff --git a/lib/meta.js b/lib/meta.js new file mode 100644 index 00000000..b89e4725 --- /dev/null +++ b/lib/meta.js @@ -0,0 +1,360 @@ +$(function(){ + var m = window.meta = {edit:[], os:{}}, ua = '', u; + try{ua = navigator.userAgent.toLowerCase()}catch(e){} + m.os.is = { + win: (ua.search("win") >= 0)? "windows":false, + lin: (ua.search("linux") >= 0)? "linux":false, + mac: (ua.search("mac") >= 0)? "macintosh":false, + and: (ua.search("android") >= 0)? "android":false, + ios: (ua.search('ipod') >= 0 + || ua.search('iphone') >= 0 + || ua.search('ipad') >= 0)? "ios":false + } + var k = m.key = {ctrl: 17, cmd: 91}; + k.meta = (m.os.is.win||m.os.is.lin||m.os.is.and)? k.ctrl : k.cmd; + k.down = function(eve){ + if($(eve.target).is('input') || eve.repeat){ return } + (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode; + if(!eve.fake && eve.which === k.last){ return } + if(k.meta === (k.last = eve.which)){ k.down.meta = m.flip(k.wipe()) || true } + if(m.flip.is()){ + (k.combo || (k.combo = [])).push(eve.which); + m.check('on', eve.which, k.at || (k.at = m.edit)); + } + if(eve.metaKey && (k.meta !== eve.which)){ k.up(eve) } // on some systems, meta hijacks keyup + } + k.up = function(eve){ var tmp; + if($(eve.target).is('input')){ return } + k.eve = m.eve = eve; + k.last = null; + eve.which = eve.which || eve.fake || eve.keyCode; + if(m.flip.is()){ m.check('up', eve.which) } + if(tmp = (k.meta === eve.which)){ k.down.meta = false } + if(tmp && k.at === m.edit){ k.wipe() } + if(27 === eve.which){ return m.flip(false) } + } + m.flip = function(tmp, aid){ + if(aid){ + m.flip.aid = true; + setTimeout(function(){$(document).one('click',function(eve){m.flip(m.flip.aid = false)})},250); // ugly but important for visual aid. + } + var board = $('#meta .meta-menu'); + ((tmp === false) || (!tmp && board.is(':visible')))? + board.addClass('meta-none') + : board.removeClass('meta-none'); + } + m.flip.is = function(){ + if(m.flip.aid && ((m.eve||{}).fake || k.at !== m.edit)){ m.flip.aid = false } + return !m.flip.aid && $('#meta .meta-menu').is(':visible'); + } + m.flip.wait = 500; + m.check = function(how, key, at){ + at = k.at || m.edit; + //m.list(at); + var edit = at[key], tmp; + if(!edit){ return } + if(k.eve && k.eve.preventDefault){ k.eve.preventDefault() } + if(edit[how]){ + edit[how](m.eve); + if(k.at !== m.edit && 'up' === how){ + if(k.down.meta){ m.list(k.at = m.edit) } + else { k.wipe() } + } + } + if('up' != how){ return } + edit.back = at; + m.list(edit, at); + } + m.list = function(at){ + var l = []; + $.each(at, function(i,k){ 'back' != i && k.combo && l.push(k) }); + if(!l.length){ return } + k.at = at; + l = l.sort(function(a,b){ + a = a.combo.slice(-1)[0] || 0; + if(a.length){ a = a.toUpperCase().charCodeAt(0) } + b = b.combo.slice(-1)[0] || 0; + if(b.length){ b = b.toUpperCase().charCodeAt(0) } + return (a < b)? -1 : 1; + }); + var $ul = $('#meta .meta-menu ul') + $ul.children('li').addClass('meta-none').hide(); setTimeout(function(){ $ul.children('.meta-none').remove() },250); // necessary fix for weird bug glitch + $.each(l, function(i, k){ + $ul.append($('
  • ').text(k.name)); + }); + if(!at.back){ return } + $ul.append($('
  • ').html('←').one('click', function(){ m.list(k.at = at.back) })); + } + m.ask = function(help, cb){ + var $ul = $('#meta .meta-menu ul').empty(); + var $put = $('').attr('id', 'meta-ask').attr('placeholder', help); + var $form = $('
    ').append($put).on('submit', function(eve){ + eve.preventDefault(); + cb($put.val()); + $li.remove(); + k.wipe(); + }); + var $li = $('
  • ').append($form); + $ul.append($li); + $put.focus(); + } + k.wipe = function(){ + k.combo = []; + m.flip(false); + m.flip.aid = false; + m.list(k.at = m.edit); + }; + $(document).on('keydown', k.down).on('keyup', k.up); + m.tap = {}; + m.tap.select = function(eve){ + m.tap.range = null; + if(!(m.tap.text()||'').trim()){ + if(m.tap.was){ + m.tap.was = null; + m.flip(false); + } + return; + } + m.flip(m.tap.range = monotype((eve||{}).target), m.tap.was = true); + } + m.tap.text = function(tmp){ + return ((tmp = window.getSelection) && tmp().toString()) || + ((tmp = document.selection) && tmp.createRange().text) || ''; + } + $(window).on('blur', k.wipe).on('focus', k.wipe); + $(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.tap.select); + //.on('keydown', '[contenteditable=true]', function(e){}); + $(document).on('touchstart', '#meta .meta-start', function(eve){ m.tap.stun = true }); + $(document).on('click', '#meta .meta-menu li', function(eve){ + if(m.tap.stun){ return m.tap.stun = false } + if(!(eve.fake = eve.which = (($(this).text().match(/[A-Z]/)||{})[0]||'').toUpperCase().charCodeAt(0))){ return } + eve.tap = true; + k.down(eve); + k.up(eve); + }); + meta.edit = function(edit){ + var tmp = edit.combow = []; + $.each(edit.combo || (edit.combo = []), function(i,k){ + if(!k || !k.length){ return } + tmp.push(k.toUpperCase().charCodeAt(0)); + }); + var at = meta.edit, l = edit.combo.length; + $.each(tmp, function(i,k){ at = at[k] = (++i >= l)? edit : at[k] || {} }); + edit.combow = edit.combow.join(','); + m.list(meta.edit); + } + meta.text = {zws: '​'}; + meta.text.editor = function(opt, as){ var tmp; + if(!opt){ return } + opt = (typeof opt == 'string')? {edit: opt} : opt.tag? opt : {tag: opt}; + var r = opt.range = opt.range || m.tap.range || monotype(), cmd = opt.edit; + as = opt.as = opt.as || as; + if(cmd && document.execCommand){ + r.restore(); + if(document.execCommand(cmd, null, as||null)){ return } + } + if(!opt.tag){ return } + opt.tag = $(opt.tag); + opt.name = opt.name || opt.tag.prop('tagName'); + if((tmp = $(r.get()).closest(opt.name)).length){ + if(r.s === r.e){ + tmp.after(meta.text.zws); + r = r.select(monotype.next(tmp[0]),1); + } else { + tmp.contents().unwrap(opt.name); + } + } else + if(r.s === r.e){ + r.insert(opt.tag); + r = r.select(opt.tag); + } else { + r.wrap(opt.tag); + } + r.restore(); + opt.range = null; + if(m.tap.range){ m.tap.range = monotype() } + } + ;(function(){try{ + /* UI */ + if(meta.css){ return } + var $m = $('
    ').attr('id', 'meta'); + $m.append($('').text('+').addClass('meta-start')); + $m.append($('
    ').addClass('meta-menu meta-none').append('
      ')); + $(document.body).append($m); + css({ + '#meta': { + display: 'block', + position: 'fixed', + bottom: '2em', + right: '2em', + background: 'white', + 'font-size': '18pt', + 'font-family': 'Tahoma, arial', + 'box-shadow': '0px 0px 1px #000044', + 'border-radius': '1em', + 'text-align': 'center', + 'z-index': 999999, + margin: 0, + padding: 0, + width: '2em', + height: '2em', + opacity: 0.7, + color: '#000044', + overflow: 'visible', + transition: 'all 0.2s ease-in' + }, + '#meta .meta-none': {display: 'none'}, + '#meta span': {'line-height': '2em'}, + '#meta .meta-menu': { + background: 'rgba(0,0,0,0.1)', + width: '12em', + right: '-2em', + bottom: '-2em', + overflow: 'visible', + position: 'absolute', + 'overflow-y': 'scroll', + 'text-align': 'right', + 'min-height': '20em', + height: '100vh' + }, + '#meta .meta-menu ul': { + padding: 0, + margin: '1em 1em 2em 0', + 'list-style-type': 'none' + }, + '#meta .meta-menu ul li': { + display: 'block', + background: 'white', + padding: '0.5em 1em', + 'border-radius': '1em', + 'margin-left': '0.25em', + 'margin-top': '0.25em', + 'float': 'right' + }, + '#meta a': {color: 'black'}, + '#meta:hover': {opacity: 1}, + '#meta .meta-menu ul:before': { + content: "' '", + display: 'block', + 'min-height': '15em', + height: '50vh' + }, + '#meta li': { + background: 'white', + padding: '0.5em 1em', + 'border-radius': '1em', + 'margin-left': '0.25em', + 'margin-top': '0.25em', + 'float': 'right' + }, + '#meta:hover .meta-menu': {display: 'block'} + }); + function css(css){ + var tmp = ''; + $.each(css, function(c,r){ + tmp += c + ' {\n'; + $.each(r, function(k,v){ + tmp += '\t'+ k +': '+ v +';\n'; + }); + tmp += '}\n'; + }); + (node = document.createElement('style')).innerHTML = tmp; + document.body.appendChild(node); + } + }catch(e){}}()); + ;(function(){ + // on fires when shortcut keydowns or on touch after command selected and then touchdown + meta.edit({ + name: "Bold", + combo: ['B'], + on: function(e){ + meta.text.editor('bold'); + }, + up: function(){} + }); + meta.edit({ + name: "Italic", + combo: ['I'], + on: function(e){ + meta.text.editor('italic'); + }, + up: function(){} + }); + meta.edit({ + name: "Underline", + combo: ['U'], + on: function(e){ + meta.text.editor('underline'); + }, + up: function(){} + }); + meta.edit({ + name: "linK", + combo: ['K'], + up: function(e){ + var range = meta.tap.range || monotype(); + meta.ask('Paste or type link...', function(url){ + meta.text.editor({tag: $('link'), edit: url? 'createLink' : 'unlink', as: url, range: range}); + }) + }, + on: function(){} + }); + meta.edit({name: "aliGn", combo: ['G']}); + meta.edit({ + name: "Left", + combo: ['G','L'], + on: function(e){ meta.text.editor('justifyLeft') }, + up: function(){} + }); + meta.edit({ + name: "Right", + combo: ['G','R'], + on: function(e){ meta.text.editor('justifyRight') }, + up: function(){ } + }); + meta.edit({ + name: "Middle", + combo: ['G','M'], + on: function(e){ meta.text.editor('justifyCenter') }, + up: function(){ } + }); + meta.edit({ + name: "Justify", + combo: ['G','J'], + on: function(e){ meta.text.editor('justifyFull') }, + up: function(){} + }); + // Align Number + // Align Points + // Align Strike + meta.edit({name: "Size", combo: ['S']}); + meta.edit({ + name: "Small", + combo: ['S','S'], + on: function(e){ meta.text.editor('fontSize', 2) }, + up: function(){ } + }); + meta.edit({ + name: "Normal", + combo: ['S','N'], + on: function(e){ meta.text.editor('fontSize', 5) }, + up: function(){} + }); + meta.edit({ + name: "Header", + combo: ['S','H'], + on: function(e){ meta.text.editor('fontSize', 6) }, + up: function(){} + }); + meta.edit({ + name: "Title", + combo: ['S','T'], + on: function(e){ meta.text.editor('fontSize', 7) }, + up: function(){} + }); + // Size Spacing + // Size Super + // Size Sub + meta.edit({name: "Edit", combo: ['E']}); + }()); +}); \ No newline at end of file diff --git a/lib/monotype.js b/lib/monotype.js index fd5a50e7..3b02bf40 100644 --- a/lib/monotype.js +++ b/lib/monotype.js @@ -121,13 +121,12 @@ return o; } return monotype; -})(function(r,opt){ - r = r || {}; +})(function(e,opt){ + var r = {}, t, m = monotype; opt = opt || {}; - monotype.win = opt.win || window; - r = r.jquery || monotype.text(r)? {root: $(r)} : r; - r.root = $(r.root || monotype.win.document.body); - var t, m = monotype; + m.win = opt.win || window; + r = (e||r).jquery || m.text(e)? {root: $(e||m.win.document.body)} : r; + r.root = $(r.root || m.win.document.body); //console.log('_______________________'); r.R = m.range(0); r.H = {}; @@ -226,5 +225,86 @@ m.restore(R); return r; } - return r; -}); \ No newline at end of file + return monotype.late(r,opt); + //return r; +}); +monotype.late = function(r,opt){ + var u, m = r //monotype(r,opt) + , strhml = function(t){ + return (t[0] === '<' && $(t).length) + }, jqtxt = function(n){ + return n.jquery?n:(strhml(n))?$('
      '+n+'
      ').contents():$(document.createTextNode(n)); + } + m.get = function(d){ + if(u === d){ return $([m.R.startContainer, m.R.endContainer]) } + return monotype.deep((d = (d && d > 0))? m.R.endContainer : m.R.startContainer + , d? m.R.endOffset : m.R.startOffset).container; + } + m.remove = function(n,R){ + R = m.R || m.range(); + R.deleteContents(); + monotype.restore(R); + m = monotype(m,opt); + return m; + } + m.insert = function(n,R){ + n = jqtxt(n); + R = m.R || m.range(); + R.deleteContents(); + $(n.get().reverse()).each(function(){ + R.insertNode(this); + }); + R.selectNodeContents(n.last()[0]); + monotype.restore(R); + m = monotype(m,opt); + return m; + } + m.wrap = function(n,R){ + var jq; + n = jqtxt(n); + n = n[0]; + R = m.R || m.range(); + if(monotype.text(R.startContainer) || monotype.text(R.endContainer)){ + var b = R.cloneContents(); + R.deleteContents(); + jq = $(n); + jq.html(b); + jq = jq[0]; + R.insertNode(jq); + }else{ + R.surroundContents(n); + } + R.selectNodeContents(jq||n); + monotype.restore(R); + m = monotype(m,opt); + return m; + } + m.select = function(n,i,e,j){ + var R = m.R || m.range(), t = e; + n = $(n); + if($.isNumeric(e)){ + e = j || n; + j = t; + } else { + e = e || n; + } + j = $.isNumeric(j)? j : $.isNumeric(i)? i : Infinity; + i = i || 0; + if(i < 0){ + t = n.contents().length || n.text().length; + i = t + i; + } if(j < 0){ + t = n.contents().length || n.text().length; + j = t + j; + } if(j === Infinity){ + R.selectNodeContents(n[0]); + } else { + R.setStart(n[0],i); + R.setEnd(e[0],j); + } + monotype.restore(R); + m = monotype(m,opt); + return m; + } + return m; +} \ No newline at end of file diff --git a/package.json b/package.json index 183c3fa7..f3c57aa0 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "name": "gun", - "version": "0.9.999998", + "version": "0.9.999999", "description": "A realtime, decentralized, offline-first, graph data synchronization engine.", "main": "index.js", "browser": "gun.min.js", "scripts": { "start": "node examples/http.js 8765", + "https": "HTTPS_KEY=test/https/server.key HTTPS_CERT=test/https/server.crt npm start", "prepublishOnly": "npm run unbuild", "test": "mocha", "testsea": "mocha test/sea.js", @@ -48,10 +49,10 @@ "node": ">=0.8.4" }, "dependencies": { - "text-encoding": "^0.7.0", "ws": "~>5.2.0" }, "optionalDependencies": { + "text-encoding": "^0.7.0", "node-webcrypto-ossl": "^1.0.39" }, "devDependencies": { diff --git a/test/common.js b/test/common.js index fd4a9d53..4b754270 100644 --- a/test/common.js +++ b/test/common.js @@ -1264,6 +1264,15 @@ describe('Gun', function(){ describe('API', function(){ var gopt = {wire:{put:function(n,cb){cb()},get:function(k,cb){cb()}}}; + if(Gun.window && location.search){ + console.log("LOCALHOST PEER MUST BE ON!"); + Gun.on('opt', function(root){ + if(root.opt.test_no_peer){ return this.to.next(root) } + root.opt.peers = root.opt.peers || {}; + root.opt.peers['http://localhost:8765/gun'] = {url: 'http://localhost:8765/gun'}; + this.to.next(root); + }); + } var gun = Gun(); it.skip('gun chain separation', function(done){ // TODO: UNDO! @@ -1372,7 +1381,7 @@ describe('Gun', function(){ }); describe('plural chains', function(){ - this.timeout(5000); + this.timeout(9000); it('uncached synchronous map on', function(done){ /* Biggest challenges so far: @@ -1549,6 +1558,7 @@ describe('Gun', function(){ //expect(count.Alice).to.be(1); //expect(count.Bob).to.be(1); //expect(count['undefined']).to.be(1); + if(done.c){ return } done.c = 1; done(); },10); } @@ -3580,6 +3590,7 @@ describe('Gun', function(){ }); it('Soul above but not beneath', function(done){ + this.timeout(5000); var gun = Gun(); var a = gun.get('sabnb'); @@ -3663,7 +3674,7 @@ describe('Gun', function(){ it('get map should not slowdown', function(done){ this.timeout(5000); - var gun = Gun().get('g/m/no/slow'); + var gun = Gun({test_no_peer:true}).get('g/m/no/slow'); //console.log("---------- setup data done -----------"); var prev, diff, max = 25, total = 9, largest = -1, gone = {}; //var prev, diff, max = Infinity, total = 10000, largest = -1, gone = {}; @@ -3727,6 +3738,23 @@ describe('Gun', function(){ gun.get('m/s/key').put({property: 'newValue'}); }, 1000); }); + + it('Deep puts with peer should work', function(done){ + // tests in async mode now automatically connect to localhost peer. + //var gun = Gun('http://localhost:8765/gun'); + var gun = Gun(); + //var user = gun.user(); + //user.create('alice', 'password', function(){ + gun.get('who').get('all').put({what: "hello world!", when: Gun.state()}, function(ack){ + //user.get('who').get('all').put({what: "hello world!", when: Gun.state()}, function(ack){ + gun.get('who').get('all').once(function(data){ + expect(data.what).to.be.ok(); + expect(data.when).to.be.ok(); + done(); + }); + }); + //}); + }); return; it('Nested listener should be called', function(done){ diff --git a/test/mocha.html b/test/mocha.html index db231f5a..f09ea754 100644 --- a/test/mocha.html +++ b/test/mocha.html @@ -30,7 +30,7 @@ Gun.debug = true; console.log('async?', Gun.debug); } - mocha.run(function(a,b,c){ + var run = mocha.run(function(a,b,c){ //document.body.prepend("MARK! REMEMBER TO REMOVE RETURN!");return; var yes = confirm("REFRESH BROWSER FOR ASYNC TESTS?"); if(yes){ @@ -48,6 +48,11 @@ document.getElementById('mocha-report').id = 'mocha-report2'; Gun.debug = false; mocha.run();*/ - }) + }); + run.on("fail", function(test, err){ + console.log("!!!!!!!!!!!", test, err); + //alert(5); + }) + \ No newline at end of file diff --git a/test/tmp/canon.html b/test/tmp/canon.html new file mode 100644 index 00000000..7d45257f --- /dev/null +++ b/test/tmp/canon.html @@ -0,0 +1,37 @@ + + + + + + + + + +
      the world is a beautiful place.
      +
      The world is a beautiful place.
      +
      + + + + +
      + + + + \ No newline at end of file