ZSSRichTextEditor.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. /*!
  2. *
  3. * ZSSRichTextEditor v0.5.2
  4. * http://www.zedsaid.com
  5. *
  6. * Copyright 2014 Zed Said Studio LLC
  7. *
  8. */
  9. var zss_editor = {};
  10. // If we are using iOS or desktop
  11. zss_editor.isUsingiOS = true;
  12. // If the user is draging
  13. zss_editor.isDragging = false;
  14. // The current selection
  15. zss_editor.currentSelection;
  16. // The current editing image
  17. zss_editor.currentEditingImage;
  18. // The current editing link
  19. zss_editor.currentEditingLink;
  20. // The objects that are enabled
  21. zss_editor.enabledItems = {};
  22. // Height of content window, will be set by viewController
  23. zss_editor.contentHeight = 244;
  24. // Sets to true when extra footer gap shows and requires to hide
  25. zss_editor.updateScrollOffset = false;
  26. /**
  27. * The initializer function that must be called onLoad
  28. */
  29. zss_editor.init = function() {
  30. $('#zss_editor_content').on('touchend', function(e) {
  31. zss_editor.enabledEditingItems(e);
  32. var clicked = $(e.target);
  33. if (!clicked.hasClass('zs_active')) {
  34. $('img').removeClass('zs_active');
  35. }
  36. });
  37. $(document).on('selectionchange',function(e){
  38. zss_editor.calculateEditorHeightWithCaretPosition();
  39. zss_editor.setScrollPosition();
  40. });
  41. $(window).on('scroll', function(e) {
  42. zss_editor.updateOffset();
  43. });
  44. // Make sure that when we tap anywhere in the document we focus on the editor
  45. $(window).on('touchmove', function(e) {
  46. zss_editor.isDragging = true;
  47. zss_editor.updateScrollOffset = true;
  48. zss_editor.setScrollPosition();
  49. });
  50. $(window).on('touchstart', function(e) {
  51. zss_editor.isDragging = false;
  52. });
  53. $(window).on('touchend', function(e) {
  54. if (!zss_editor.isDragging && (e.target.id == "zss_editor_footer"||e.target.nodeName.toLowerCase() == "html")) {
  55. zss_editor.focusEditor();
  56. }
  57. });
  58. }//end
  59. zss_editor.updateOffset = function() {
  60. if (!zss_editor.updateScrollOffset)
  61. return;
  62. var offsetY = window.document.body.scrollTop;
  63. var footer = $('#zss_editor_footer');
  64. var maxOffsetY = footer.offset().top - zss_editor.contentHeight;
  65. if (maxOffsetY < 0)
  66. maxOffsetY = 0;
  67. if (offsetY > maxOffsetY)
  68. {
  69. window.scrollTo(0, maxOffsetY);
  70. }
  71. zss_editor.setScrollPosition();
  72. }
  73. // This will show up in the XCode console as we are able to push this into an NSLog.
  74. zss_editor.debug = function(msg) {
  75. window.location = 'debug://'+msg;
  76. }
  77. zss_editor.setScrollPosition = function() {
  78. var position = window.pageYOffset;
  79. window.location = 'scroll://'+position;
  80. }
  81. zss_editor.setPlaceholder = function(placeholder) {
  82. var editor = $('#zss_editor_content');
  83. //set placeHolder
  84. if(editor.text().length == 1){
  85. editor.text(placeholder);
  86. editor.css("color","gray");
  87. }
  88. //set focus
  89. editor.focus(function(){
  90. if($(this).text() == placeholder){
  91. $(this).text("");
  92. $(this).css("color","black");
  93. }
  94. }).focusout(function(){
  95. if(!$(this).text().length){
  96. $(this).text(placeholder);
  97. $(this).css("color","gray");
  98. }
  99. });
  100. }
  101. zss_editor.setFooterHeight = function(footerHeight) {
  102. var footer = $('#zss_editor_footer');
  103. footer.height(footerHeight + 'px');
  104. }
  105. zss_editor.getCaretYPosition = function() {
  106. var sel = window.getSelection();
  107. // Next line is comented to prevent deselecting selection. It looks like work but if there are any issues will appear then uconmment it as well as code above.
  108. //sel.collapseToStart();
  109. var range = sel.getRangeAt(0);
  110. var span = document.createElement('span');// something happening here preventing selection of elements
  111. range.collapse(false);
  112. range.insertNode(span);
  113. var topPosition = span.offsetTop;
  114. span.parentNode.removeChild(span);
  115. return topPosition;
  116. }
  117. zss_editor.calculateEditorHeightWithCaretPosition = function() {
  118. var padding = 50;
  119. var c = zss_editor.getCaretYPosition();
  120. var e = document.getElementById('zss_editor_content');
  121. var editor = $('#zss_editor_content');
  122. var offsetY = window.document.body.scrollTop;
  123. var height = zss_editor.contentHeight;
  124. var newPos = window.pageYOffset;
  125. if (c < offsetY) {
  126. newPos = c;
  127. } else if (c > (offsetY + height - padding)) {
  128. var newPos = c - height + padding - 18;
  129. }
  130. window.scrollTo(0, newPos);
  131. }
  132. zss_editor.backuprange = function(){
  133. var selection = window.getSelection();
  134. var range = selection.getRangeAt(0);
  135. zss_editor.currentSelection = {"startContainer": range.startContainer, "startOffset":range.startOffset,"endContainer":range.endContainer, "endOffset":range.endOffset};
  136. }
  137. zss_editor.restorerange = function(){
  138. var selection = window.getSelection();
  139. selection.removeAllRanges();
  140. var range = document.createRange();
  141. range.setStart(zss_editor.currentSelection.startContainer, zss_editor.currentSelection.startOffset);
  142. range.setEnd(zss_editor.currentSelection.endContainer, zss_editor.currentSelection.endOffset);
  143. selection.addRange(range);
  144. }
  145. zss_editor.getSelectedNode = function() {
  146. var node,selection;
  147. if (window.getSelection) {
  148. selection = getSelection();
  149. node = selection.anchorNode;
  150. }
  151. if (!node && document.selection) {
  152. selection = document.selection
  153. var range = selection.getRangeAt ? selection.getRangeAt(0) : selection.createRange();
  154. node = range.commonAncestorContainer ? range.commonAncestorContainer :
  155. range.parentElement ? range.parentElement() : range.item(0);
  156. }
  157. if (node) {
  158. return (node.nodeName == "#text" ? node.parentNode : node);
  159. }
  160. };
  161. zss_editor.setBold = function() {
  162. document.execCommand('bold', false, null);
  163. zss_editor.enabledEditingItems();
  164. }
  165. zss_editor.setItalic = function() {
  166. document.execCommand('italic', false, null);
  167. zss_editor.enabledEditingItems();
  168. }
  169. zss_editor.setSubscript = function() {
  170. document.execCommand('subscript', false, null);
  171. zss_editor.enabledEditingItems();
  172. }
  173. zss_editor.setSuperscript = function() {
  174. document.execCommand('superscript', false, null);
  175. zss_editor.enabledEditingItems();
  176. }
  177. zss_editor.setStrikeThrough = function() {
  178. document.execCommand('strikeThrough', false, null);
  179. zss_editor.enabledEditingItems();
  180. }
  181. zss_editor.setUnderline = function() {
  182. document.execCommand('underline', false, null);
  183. zss_editor.enabledEditingItems();
  184. }
  185. zss_editor.setBlockquote = function() {
  186. document.execCommand('formatBlock', false, '<blockquote>');
  187. zss_editor.enabledEditingItems();
  188. }
  189. zss_editor.removeFormating = function() {
  190. document.execCommand('removeFormat', false, null);
  191. zss_editor.enabledEditingItems();
  192. }
  193. zss_editor.setHorizontalRule = function() {
  194. document.execCommand('insertHorizontalRule', false, null);
  195. zss_editor.enabledEditingItems();
  196. }
  197. zss_editor.setHeading = function(heading) {
  198. var current_selection = $(zss_editor.getSelectedNode());
  199. var t = current_selection.prop("tagName").toLowerCase();
  200. var is_heading = (t == 'h1' || t == 'h2' || t == 'h3' || t == 'h4' || t == 'h5' || t == 'h6');
  201. if (is_heading && heading == t) {
  202. var c = current_selection.html();
  203. current_selection.replaceWith(c);
  204. } else {
  205. document.execCommand('formatBlock', false, '<'+heading+'>');
  206. }
  207. zss_editor.enabledEditingItems();
  208. }
  209. zss_editor.setParagraph = function() {
  210. var current_selection = $(zss_editor.getSelectedNode());
  211. var t = current_selection.prop("tagName").toLowerCase();
  212. var is_paragraph = (t == 'p');
  213. if (is_paragraph) {
  214. var c = current_selection.html();
  215. current_selection.replaceWith(c);
  216. } else {
  217. document.execCommand('formatBlock', false, '<p>');
  218. }
  219. zss_editor.enabledEditingItems();
  220. }
  221. // Need way to remove formatBlock
  222. console.log('WARNING: We need a way to remove formatBlock items');
  223. zss_editor.undo = function() {
  224. document.execCommand('undo', false, null);
  225. zss_editor.enabledEditingItems();
  226. }
  227. zss_editor.redo = function() {
  228. document.execCommand('redo', false, null);
  229. zss_editor.enabledEditingItems();
  230. }
  231. zss_editor.setOrderedList = function() {
  232. document.execCommand('insertOrderedList', false, null);
  233. zss_editor.enabledEditingItems();
  234. }
  235. zss_editor.setUnorderedList = function() {
  236. document.execCommand('insertUnorderedList', false, null);
  237. zss_editor.enabledEditingItems();
  238. }
  239. zss_editor.setJustifyCenter = function() {
  240. document.execCommand('justifyCenter', false, null);
  241. zss_editor.enabledEditingItems();
  242. }
  243. zss_editor.setJustifyFull = function() {
  244. document.execCommand('justifyFull', false, null);
  245. zss_editor.enabledEditingItems();
  246. }
  247. zss_editor.setJustifyLeft = function() {
  248. document.execCommand('justifyLeft', false, null);
  249. zss_editor.enabledEditingItems();
  250. }
  251. zss_editor.setJustifyRight = function() {
  252. document.execCommand('justifyRight', false, null);
  253. zss_editor.enabledEditingItems();
  254. }
  255. zss_editor.setIndent = function() {
  256. document.execCommand('indent', false, null);
  257. zss_editor.enabledEditingItems();
  258. }
  259. zss_editor.setOutdent = function() {
  260. document.execCommand('outdent', false, null);
  261. zss_editor.enabledEditingItems();
  262. }
  263. zss_editor.setTextColor = function(color) {
  264. zss_editor.restorerange();
  265. document.execCommand("styleWithCSS", null, true);
  266. document.execCommand('foreColor', false, color);
  267. document.execCommand("styleWithCSS", null, false);
  268. zss_editor.enabledEditingItems();
  269. // document.execCommand("removeFormat", false, "foreColor"); // Removes just foreColor
  270. }
  271. zss_editor.setBackgroundColor = function(color) {
  272. zss_editor.restorerange();
  273. document.execCommand("styleWithCSS", null, true);
  274. document.execCommand('hiliteColor', false, color);
  275. document.execCommand("styleWithCSS", null, false);
  276. zss_editor.enabledEditingItems();
  277. }
  278. // Needs addClass method
  279. zss_editor.insertLink = function(url, title) {
  280. zss_editor.restorerange();
  281. var sel = document.getSelection();
  282. console.log(sel);
  283. if (sel.toString().length != 0) {
  284. if (sel.rangeCount) {
  285. var el = document.createElement("a");
  286. el.setAttribute("href", url);
  287. el.setAttribute("title", title);
  288. var range = sel.getRangeAt(0).cloneRange();
  289. range.surroundContents(el);
  290. sel.removeAllRanges();
  291. sel.addRange(range);
  292. }
  293. }
  294. zss_editor.enabledEditingItems();
  295. }
  296. zss_editor.updateLink = function(url, title) {
  297. zss_editor.restorerange();
  298. if (zss_editor.currentEditingLink) {
  299. var c = zss_editor.currentEditingLink;
  300. c.attr('href', url);
  301. c.attr('title', title);
  302. }
  303. zss_editor.enabledEditingItems();
  304. }//end
  305. zss_editor.updateImage = function(url, alt) {
  306. zss_editor.restorerange();
  307. if (zss_editor.currentEditingImage) {
  308. var c = zss_editor.currentEditingImage;
  309. c.attr('src', url);
  310. c.attr('alt', alt);
  311. }
  312. zss_editor.enabledEditingItems();
  313. }//end
  314. zss_editor.unlink = function() {
  315. if (zss_editor.currentEditingLink) {
  316. var c = zss_editor.currentEditingLink;
  317. c.contents().unwrap();
  318. }
  319. zss_editor.enabledEditingItems();
  320. }
  321. zss_editor.quickLink = function() {
  322. var sel = document.getSelection();
  323. var link_url = "";
  324. var test = new String(sel);
  325. var mailregexp = new RegExp("^(.+)(\@)(.+)$", "gi");
  326. if (test.search(mailregexp) == -1) {
  327. checkhttplink = new RegExp("^http\:\/\/", "gi");
  328. if (test.search(checkhttplink) == -1) {
  329. checkanchorlink = new RegExp("^\#", "gi");
  330. if (test.search(checkanchorlink) == -1) {
  331. link_url = "http://" + sel;
  332. } else {
  333. link_url = sel;
  334. }
  335. } else {
  336. link_url = sel;
  337. }
  338. } else {
  339. checkmaillink = new RegExp("^mailto\:", "gi");
  340. if (test.search(checkmaillink) == -1) {
  341. link_url = "mailto:" + sel;
  342. } else {
  343. link_url = sel;
  344. }
  345. }
  346. var html_code = '<a href="' + link_url + '">' + sel + '</a>';
  347. zss_editor.insertHTML(html_code);
  348. }
  349. zss_editor.prepareInsert = function() {
  350. zss_editor.backuprange();
  351. }
  352. zss_editor.insertImage = function(url, alt) {
  353. zss_editor.restorerange();
  354. var html = '<img src="'+url+'" alt="'+alt+'" />';
  355. zss_editor.insertHTML(html);
  356. zss_editor.enabledEditingItems();
  357. }
  358. zss_editor.setHTML = function(html) {
  359. var editor = $('#zss_editor_content');
  360. editor.html(html);
  361. }
  362. zss_editor.insertHTML = function(html) {
  363. document.execCommand('insertHTML', false, html);
  364. zss_editor.enabledEditingItems();
  365. }
  366. zss_editor.getHTML = function() {
  367. // Images
  368. var img = $('img');
  369. if (img.length != 0) {
  370. $('img').removeClass('zs_active');
  371. $('img').each(function(index, e) {
  372. var image = $(this);
  373. var zs_class = image.attr('class');
  374. if (typeof(zs_class) != "undefined") {
  375. if (zs_class == '') {
  376. image.removeAttr('class');
  377. }
  378. }
  379. });
  380. }
  381. // Blockquote
  382. var bq = $('blockquote');
  383. if (bq.length != 0) {
  384. bq.each(function() {
  385. var b = $(this);
  386. if (b.css('border').indexOf('none') != -1) {
  387. b.css({'border': ''});
  388. }
  389. if (b.css('padding').indexOf('0px') != -1) {
  390. b.css({'padding': ''});
  391. }
  392. });
  393. }
  394. // Get the contents
  395. var h = document.getElementById("zss_editor_content").innerHTML;
  396. return h;
  397. }
  398. zss_editor.getText = function() {
  399. return $('#zss_editor_content').text();
  400. }
  401. zss_editor.isCommandEnabled = function(commandName) {
  402. return document.queryCommandState(commandName);
  403. }
  404. zss_editor.enabledEditingItems = function(e) {
  405. console.log('enabledEditingItems');
  406. var items = [];
  407. if (zss_editor.isCommandEnabled('bold')) {
  408. items.push('bold');
  409. }
  410. if (zss_editor.isCommandEnabled('italic')) {
  411. items.push('italic');
  412. }
  413. if (zss_editor.isCommandEnabled('subscript')) {
  414. items.push('subscript');
  415. }
  416. if (zss_editor.isCommandEnabled('superscript')) {
  417. items.push('superscript');
  418. }
  419. if (zss_editor.isCommandEnabled('strikeThrough')) {
  420. items.push('strikeThrough');
  421. }
  422. if (zss_editor.isCommandEnabled('underline')) {
  423. items.push('underline');
  424. }
  425. if (zss_editor.isCommandEnabled('insertOrderedList')) {
  426. items.push('orderedList');
  427. }
  428. if (zss_editor.isCommandEnabled('insertUnorderedList')) {
  429. items.push('unorderedList');
  430. }
  431. if (zss_editor.isCommandEnabled('justifyCenter')) {
  432. items.push('justifyCenter');
  433. }
  434. if (zss_editor.isCommandEnabled('justifyFull')) {
  435. items.push('justifyFull');
  436. }
  437. if (zss_editor.isCommandEnabled('justifyLeft')) {
  438. items.push('justifyLeft');
  439. }
  440. if (zss_editor.isCommandEnabled('justifyRight')) {
  441. items.push('justifyRight');
  442. }
  443. if (zss_editor.isCommandEnabled('insertHorizontalRule')) {
  444. items.push('horizontalRule');
  445. }
  446. var formatBlock = document.queryCommandValue('formatBlock');
  447. if (formatBlock.length > 0) {
  448. items.push(formatBlock);
  449. }
  450. // Images
  451. $('img').bind('touchstart', function(e) {
  452. $('img').removeClass('zs_active');
  453. $(this).addClass('zs_active');
  454. });
  455. // Use jQuery to figure out those that are not supported
  456. if (typeof(e) != "undefined") {
  457. // The target element
  458. var t = $(e.target);
  459. var nodeName = e.target.nodeName.toLowerCase();
  460. // Background Color
  461. var bgColor = t.css('backgroundColor');
  462. if (bgColor.length != 0 && bgColor != 'rgba(0, 0, 0, 0)' && bgColor != 'rgb(0, 0, 0)' && bgColor != 'transparent') {
  463. items.push('backgroundColor');
  464. }
  465. // Text Color
  466. var textColor = t.css('color');
  467. if (textColor.length != 0 && textColor != 'rgba(0, 0, 0, 0)' && textColor != 'rgb(0, 0, 0)' && textColor != 'transparent') {
  468. items.push('textColor');
  469. }
  470. // Link
  471. if (nodeName == 'a') {
  472. zss_editor.currentEditingLink = t;
  473. var title = t.attr('title');
  474. items.push('link:'+t.attr('href'));
  475. if (t.attr('title') !== undefined) {
  476. items.push('link-title:'+t.attr('title'));
  477. }
  478. } else {
  479. zss_editor.currentEditingLink = null;
  480. }
  481. // Blockquote
  482. if (nodeName == 'blockquote') {
  483. items.push('indent');
  484. }
  485. // Image
  486. if (nodeName == 'img') {
  487. zss_editor.currentEditingImage = t;
  488. items.push('image:'+t.attr('src'));
  489. if (t.attr('alt') !== undefined) {
  490. items.push('image-alt:'+t.attr('alt'));
  491. }
  492. } else {
  493. zss_editor.currentEditingImage = null;
  494. }
  495. }
  496. if (items.length > 0) {
  497. if (zss_editor.isUsingiOS) {
  498. //window.location = "zss-callback/"+items.join(',');
  499. window.location = "callback://0/"+items.join(',');
  500. } else {
  501. console.log("callback://"+items.join(','));
  502. }
  503. } else {
  504. if (zss_editor.isUsingiOS) {
  505. window.location = "zss-callback/";
  506. } else {
  507. console.log("callback://");
  508. }
  509. }
  510. }
  511. zss_editor.focusEditor = function() {
  512. // the following was taken from http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity/3866442#3866442
  513. // and ensures we move the cursor to the end of the editor
  514. var editor = $('#zss_editor_content');
  515. var range = document.createRange();
  516. range.selectNodeContents(editor.get(0));
  517. range.collapse(false);
  518. var selection = window.getSelection();
  519. selection.removeAllRanges();
  520. selection.addRange(range);
  521. editor.focus();
  522. }
  523. zss_editor.blurEditor = function() {
  524. $('#zss_editor_content').blur();
  525. }//end