meta.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. $(function(){
  2. var noop = function(){}, u;
  3. var m = window.meta = {edit:[]};
  4. var k = m.key = {};
  5. k.meta = {17:17, 91:17, 93:17, 224:17};
  6. k.down = function(eve){
  7. if(eve.repeat){ return }
  8. var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
  9. if(!eve.fake && key === k.last){ return } k.last = key;
  10. if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
  11. if(k.meta[key]){ k.down.meta = key = -1 }
  12. if(!k.down.meta){ return }
  13. }
  14. (k.combo || (k.combo = [])).push(key);
  15. m.check('on', key, k.at || (k.at = m.edit));
  16. if(k.meta[key]){
  17. m.list(k.at.back || m.edit);
  18. if(k.at && !k.at.back){ m.flip() }
  19. }
  20. }
  21. k.up = function(eve){ var tmp;
  22. var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
  23. if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
  24. if(k.meta[key]){
  25. k.down.meta = null;
  26. key = -1;
  27. } else
  28. if(!k.down.meta){ return }
  29. }
  30. k.last = null;
  31. if($(':focus').closest('#meta').length){ return }
  32. m.check('up', key);
  33. if(-1 === key || 27 === eve.which){ k.wipe() }
  34. }
  35. m.flip = function(tmp){
  36. var board = $('#meta .meta-menu');
  37. ((tmp === false) || (!tmp && board.is(':visible')))?
  38. board.addClass('meta-none')
  39. : board.removeClass('meta-none');
  40. }
  41. m.flip.is = function(){
  42. return $('#meta .meta-menu').is(':visible');
  43. }
  44. m.flip.wait = 500;
  45. m.check = function(how, key, at){
  46. at = k.at || m.edit;
  47. var edit = at[key];
  48. if(!edit){ return }
  49. var tmp = k.eve || noop;
  50. if(tmp.preventDefault){ tmp.preventDefault() }
  51. if(edit[how]){
  52. if(tmp.fake && !edit.fake){
  53. m.tap.edit = edit;
  54. } else {
  55. edit[how](m.eve);
  56. /*if(k.at !== m.edit && 'up' === how){
  57. if(k.down.meta){ m.list(k.at = m.edit) }
  58. else { k.wipe() }
  59. }*/
  60. }
  61. }
  62. if('up' != how){ return }
  63. if(at != edit){ edit.back = at }
  64. m.list(edit, true);
  65. }
  66. m.list = function(at, opt){
  67. if(!at){ return m.flip(false) }
  68. var l = [];
  69. $.each(at, function(i,k){ 'back' != i && k.combo && k.name && l.push(k) });
  70. if(!l.length){ return }
  71. k.at = at;
  72. l = l.sort(function(a,b){
  73. a = a.combo.slice(-1)[0] || 0;
  74. if(a.length){ a = a.toUpperCase().charCodeAt(0) }
  75. b = b.combo.slice(-1)[0] || 0;
  76. if(b.length){ b = b.toUpperCase().charCodeAt(0) }
  77. return (a < b)? -1 : 1;
  78. });
  79. var $ul = $('#meta .meta-menu ul')
  80. $ul.children('li').addClass('meta-none').hide(); setTimeout(function(){ $ul.children('.meta-none').remove() },250); // necessary fix for weird bug glitch
  81. $.each(l, function(i, k){
  82. $ul.append($('<li>').text(k.name));
  83. });
  84. if(opt){ m.flip(true) }
  85. $ul.append($('<li>').html('&larr;').one('click', function(){
  86. m.list(k.at = at.back);
  87. }));
  88. }
  89. m.ask = function(help, cb){
  90. var $ul = $('#meta .meta-menu ul').empty();
  91. var $put = $('<input>').attr('id', 'meta-ask').attr('placeholder', help);
  92. var $form = $('<form>').append($put).on('submit', function(eve){
  93. eve.preventDefault();
  94. cb($put.val());
  95. $li.remove();
  96. k.wipe();
  97. });
  98. var $li = $('<li>').append($form);
  99. $ul.append($li);
  100. m.flip(true);
  101. $put.focus();
  102. }
  103. k.wipe = function(opt){
  104. k.down.meta = false;
  105. k.combo = [];
  106. if(!opt){ m.flip(false) }
  107. m.list(k.at = m.edit);
  108. };
  109. m.tap = function(){
  110. var on = $('.meta-on')
  111. .or($($(document.querySelectorAll(':hover')).get().reverse()).first())
  112. .or($(document.elementFromPoint(meta.tap.x, meta.tap.y)));
  113. return on;
  114. }
  115. $(window).on('blur', k.wipe).on('focus', k.wipe);
  116. $(document).on('mousedown mousemove mouseup', function(eve){
  117. m.tap.eve = eve;
  118. m.tap.x = eve.pageX||0;
  119. m.tap.y = eve.pageY||0;
  120. m.tap.on = $(eve.target);
  121. }).on('mousedown touchstart', function(eve){
  122. var tmp = m.tap.edit;
  123. if(!tmp || !tmp.on){ return }
  124. tmp.on(eve);
  125. m.tap.edit = null;
  126. });
  127. $(document).on('touchstart', '#meta .meta-start', function(eve){ m.tap.stun = true });
  128. $(document).on('click', '#meta .meta-menu li', function(eve){
  129. if(m.tap.stun){ return m.tap.stun = false }
  130. if(!(eve.fake = eve.which = (($(this).text().match(/[A-Z]/)||{})[0]||'').toUpperCase().charCodeAt(0))){ return }
  131. eve.tap = true;
  132. k.down(eve);
  133. k.up(eve);
  134. });
  135. $(document).on('keydown', k.down).on('keyup', k.up);
  136. meta.edit = function(edit){
  137. var tmp = edit.combow = [];
  138. $.each(edit.combo || (edit.combo = []), function(i,k){
  139. if(!k || !k.length){ if('number' == typeof k){ tmp.push(k) } return }
  140. tmp.push(k.toUpperCase().charCodeAt(0));
  141. });
  142. var at = meta.edit, l = edit.combo.length;
  143. $.each(tmp, function(i,k){ at = at[k] = (++i >= l)? edit : at[k] || {} });
  144. edit.combow = edit.combow.join(',');
  145. m.list(meta.edit);
  146. }
  147. $.fn.or = function(s){ return this.length ? this : $(s||'body') };
  148. ;(function(){try{
  149. /* UI */
  150. if(meta.css){ return }
  151. var $m = $('<div>').attr('id', 'meta');
  152. $m.append($('<span>').text('+').addClass('meta-start'));
  153. $m.append($('<div>').addClass('meta-menu meta-none').append('<ul>'));
  154. $(document.body).append($m);
  155. css({
  156. '#meta': {
  157. display: 'block',
  158. position: 'fixed',
  159. bottom: '2em',
  160. right: '2em',
  161. background: 'white',
  162. 'font-size': '18pt',
  163. 'font-family': 'Tahoma, arial',
  164. 'box-shadow': '0px 0px 1px #000044',
  165. 'border-radius': '1em',
  166. 'text-align': 'center',
  167. 'z-index': 999999,
  168. margin: 0,
  169. padding: 0,
  170. width: '2em',
  171. height: '2em',
  172. opacity: 0.7,
  173. outline: 'none',
  174. color: '#000044',
  175. overflow: 'visible',
  176. transition: 'all 0.2s ease-in'
  177. },
  178. '#meta *': {outline: 'none'},
  179. '#meta .meta-none': {display: 'none'},
  180. '#meta span': {'line-height': '2em'},
  181. '#meta .meta-menu': {
  182. background: 'rgba(0,0,0,0.1)',
  183. width: '12em',
  184. right: '-2em',
  185. bottom: '-2em',
  186. overflow: 'visible',
  187. position: 'absolute',
  188. 'overflow-y': 'scroll',
  189. 'text-align': 'right',
  190. 'min-height': '20em',
  191. height: '100vh'
  192. },
  193. '#meta .meta-menu ul': {
  194. padding: 0,
  195. margin: '1em 1em 2em 0',
  196. 'list-style-type': 'none'
  197. },
  198. '#meta .meta-menu ul li': {
  199. display: 'block',
  200. background: 'white',
  201. padding: '0.5em 1em',
  202. 'border-radius': '1em',
  203. 'margin-left': '0.25em',
  204. 'margin-top': '0.25em',
  205. 'float': 'right'
  206. },
  207. '#meta a': {color: 'black'},
  208. '#meta:hover': {opacity: 1},
  209. '#meta .meta-menu ul:before': {
  210. content: "' '",
  211. display: 'block',
  212. 'min-height': '15em',
  213. height: '50vh'
  214. },
  215. '#meta li': {
  216. background: 'white',
  217. padding: '0.5em 1em',
  218. 'border-radius': '1em',
  219. 'margin-left': '0.25em',
  220. 'margin-top': '0.25em',
  221. 'float': 'right'
  222. },
  223. '#meta:hover .meta-menu': {display: 'block'}
  224. });
  225. function css(css){
  226. var tmp = '';
  227. $.each(css, function(c,r){
  228. tmp += c + ' {\n';
  229. $.each(r, function(k,v){
  230. tmp += '\t'+ k +': '+ v +';\n';
  231. });
  232. tmp += '}\n';
  233. });
  234. var tag = document.createElement('style');
  235. tag.innerHTML = tmp;
  236. document.body.appendChild(tag);
  237. }
  238. }catch(e){}}());
  239. ;(function(){
  240. // include basic text editing by default.
  241. var monotype = window.monotype || function(){console.log("monotype needed")};
  242. var m = meta;
  243. m.text = {zws: '&#8203;'};
  244. m.text.on = function(eve){ var tmp;
  245. if($((eve||{}).target).closest('#meta').length){ return }
  246. m.text.range = null;
  247. if(!(m.text.copy()||'').trim()){
  248. m.flip(false);
  249. m.list(m.text.it);
  250. return;
  251. }
  252. m.text.range = monotype((eve||{}).target);
  253. m.text.it.on(eve);
  254. }
  255. m.text.copy = function(tmp){
  256. return ((tmp = window.getSelection) && tmp().toString()) ||
  257. ((tmp = document.selection) && tmp.createRange().text) || '';
  258. }
  259. $(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.text.on);
  260. m.text.editor = function(opt, as){ var tmp;
  261. if(!opt){ return }
  262. opt = (typeof opt == 'string')? {edit: opt} : opt.tag? opt : {tag: opt};
  263. var r = opt.range = opt.range || m.text.range || monotype(), cmd = opt.edit;
  264. as = opt.as = opt.as || as;
  265. if(cmd && document.execCommand){
  266. r.restore();
  267. if(document.execCommand(cmd, null, as||null)){
  268. if(m.text.range){ m.text.range = monotype() }
  269. return;
  270. }
  271. }
  272. if(!opt.tag){ return }
  273. opt.tag = $(opt.tag);
  274. opt.name = opt.name || opt.tag.prop('tagName');
  275. if((tmp = $(r.get()).closest(opt.name)).length){
  276. if(r.s === r.e){
  277. tmp.after(m.text.zws);
  278. r = r.select(monotype.next(tmp[0]),1);
  279. } else {
  280. tmp.contents().unwrap(opt.name);
  281. }
  282. } else
  283. if(r.s === r.e){
  284. r.insert(opt.tag);
  285. r = r.select(opt.tag);
  286. } else {
  287. r.wrap(opt.tag);
  288. }
  289. r.restore();
  290. opt.range = null;
  291. if(m.text.range){ m.text.range = monotype() }
  292. }
  293. meta.edit(meta.text.it = {combo: [-1], on: function(){ m.list(this, true) }, back: meta.edit}); // -1 is key for typing.
  294. meta.text.it[-1] = meta.text.it;
  295. meta.edit({
  296. name: "Bold",
  297. combo: [-1,'B'], fake: -1,
  298. on: function(eve){
  299. meta.text.editor('bold');
  300. },
  301. up: function(){}
  302. });
  303. meta.edit({
  304. name: "Italic",
  305. combo: [-1,'I'], fake: -1,
  306. on: function(eve){
  307. meta.text.editor('italic');
  308. },
  309. up: function(){}
  310. });
  311. /*meta.edit({
  312. name: "Underline",
  313. combo: [-1,'U'], fake: -1,
  314. on: function(eve){
  315. meta.text.editor('underline');
  316. },
  317. up: function(){}
  318. });*/
  319. meta.edit({
  320. name: "linK",
  321. combo: [-1,'K'], fake: -1,
  322. on: function(eve){
  323. var range = meta.text.range || monotype();
  324. meta.ask('Paste or type link...', function(url){
  325. meta.text.editor({tag: $('<a href="'+url+'">link</a>'), edit: url? 'createLink' : 'unlink', as: url, range: range});
  326. })
  327. }
  328. });
  329. //meta.edit({name: "aliGn", combo: [-1,'G']}); // MOVE TO ADVANCED MENu!
  330. meta.edit({
  331. name: "Left",
  332. combo: [-1,'G','L'], fake: -1,
  333. on: function(eve){ meta.text.editor('justifyLeft') },
  334. up: function(){}
  335. });
  336. meta.edit({
  337. name: "Right",
  338. combo: [-1,'G','R'], fake: -1,
  339. on: function(eve){ meta.text.editor('justifyRight') },
  340. up: function(){ }
  341. });
  342. meta.edit({
  343. name: "Middle",
  344. combo: [-1,'G','M'], fake: -1,
  345. on: function(eve){ meta.text.editor('justifyCenter') },
  346. up: function(){ }
  347. });
  348. meta.edit({
  349. name: "Justify",
  350. combo: [-1,'G','J'], fake: -1,
  351. on: function(eve){ meta.text.editor('justifyFull') },
  352. up: function(){}
  353. });
  354. // Align Number
  355. // Align Points
  356. // Align Strike
  357. meta.edit({name: "Size", combo: [-1,'S'], on: function(){ m.list(this, true) }});
  358. meta.edit({
  359. name: "Small",
  360. combo: [-1,'S','S'], fake: -1,
  361. on: function(eve){ meta.text.editor('fontSize', 2) },
  362. up: function(){ }
  363. });
  364. meta.edit({
  365. name: "Normal",
  366. combo: [-1,'S','N'], fake: -1,
  367. on: function(eve){ meta.text.editor('fontSize', 5) },
  368. up: function(){}
  369. });
  370. meta.edit({
  371. name: "Header",
  372. combo: [-1,'S','H'], fake: -1,
  373. on: function(eve){ meta.text.editor('fontSize', 6) },
  374. up: function(){}
  375. });
  376. meta.edit({
  377. name: "Title",
  378. combo: [-1,'S','T'], fake: -1,
  379. on: function(eve){ meta.text.editor('fontSize', 7) },
  380. up: function(){}
  381. });
  382. }());
  383. });