ZSSRichTextEditor.js 20 KB

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