From 0525b8cd324f81ddab0b8daf32f384be881103c5 Mon Sep 17 00:00:00 2001 From: "joe.prisk" Date: Wed, 28 Jan 2015 14:43:37 +0000 Subject: [PATCH] add proper html5 class - we do not live in the past --- src/customStyles.css | 11 + src/textAngularSetup.js | 1254 +++++++++++++++++++-------------------- 2 files changed, 632 insertions(+), 633 deletions(-) create mode 100644 src/customStyles.css diff --git a/src/customStyles.css b/src/customStyles.css new file mode 100644 index 00000000..442fa9fc --- /dev/null +++ b/src/customStyles.css @@ -0,0 +1,11 @@ +.wysiwyg-justify-right { + text-align:right; +} + +.wysiwyg-justify-left { + text-align: left; +} + +.wysiwyg-justify-center { + text-align: center; +} \ No newline at end of file diff --git a/src/textAngularSetup.js b/src/textAngularSetup.js index 8db64da7..aca08489 100644 --- a/src/textAngularSetup.js +++ b/src/textAngularSetup.js @@ -1,693 +1,681 @@ /* -@license textAngular -Author : Austin Anderson -License : 2013 MIT -Version 1.3.0 + @license textAngular + Author : Austin Anderson + License : 2013 MIT + Version 1.3.0 -See README.md or https://github.com/fraywing/textAngular/wiki for requirements and use. -*/ + See README.md or https://github.com/fraywing/textAngular/wiki for requirements and use. + */ angular.module('textAngularSetup', []) - + // Here we set up the global display defaults, to set your own use a angular $provider#decorator. -.value('taOptions', { - toolbar: [ - ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'pre', 'quote'], - ['bold', 'italics', 'underline', 'strikeThrough', 'ul', 'ol', 'redo', 'undo', 'clear'], - ['justifyLeft','justifyCenter','justifyRight','indent','outdent'], - ['html', 'insertImage', 'insertLink', 'insertVideo', 'wordcount', 'charcount'] - ], - classes: { - focussed: "focussed", - toolbar: "btn-toolbar", - toolbarGroup: "btn-group", - toolbarButton: "btn btn-default", - toolbarButtonActive: "active", - disabled: "disabled", - textEditor: 'form-control', - htmlEditor: 'form-control' - }, - setup: { - // wysiwyg mode - textEditorSetup: function($element){ /* Do some processing here */ }, - // raw html - htmlEditorSetup: function($element){ /* Do some processing here */ } - }, - defaultFileDropHandler: + .value('taOptions', { + toolbar: [ + ['h1', 'h2', 'h3', 'h4', 'h5', 'p', 'pre', 'quote'], + ['bold', 'italics', 'underline', 'strikeThrough', 'ul', 'ol', 'redo', 'undo', 'clear'], + ['justifyLeft','justifyCenter','justifyRight','indent','outdent'], + ['html', 'insertImage', 'insertLink', 'insertVideo', 'wordcount', 'charcount'] + ], + classes: { + focussed: "focussed", + toolbar: "btn-toolbar", + toolbarGroup: "btn-group", + toolbarButton: "btn btn-default", + toolbarButtonActive: "active", + disabled: "disabled", + textEditor: 'form-control', + htmlEditor: 'form-control' + }, + setup: { + // wysiwyg mode + textEditorSetup: function($element){ /* Do some processing here */ }, + // raw html + htmlEditorSetup: function($element){ /* Do some processing here */ } + }, + defaultFileDropHandler: /* istanbul ignore next: untestable image processing */ - function(file, insertAction){ - var reader = new FileReader(); - if(file.type.substring(0, 5) === 'image'){ - reader.onload = function() { - if(reader.result !== '') insertAction('insertImage', reader.result, true); - }; + function(file, insertAction){ + var reader = new FileReader(); + if(file.type.substring(0, 5) === 'image'){ + reader.onload = function() { + if(reader.result !== '') insertAction('insertImage', reader.result, true); + }; - reader.readAsDataURL(file); - // NOTE: For async procedures return a promise and resolve it when the editor should update the model. - return true; - } - return false; - } -}) + reader.readAsDataURL(file); + // NOTE: For async procedures return a promise and resolve it when the editor should update the model. + return true; + } + return false; + } + }) // This is the element selector string that is used to catch click events within a taBind, prevents the default and $emits a 'ta-element-select' event // these are individually used in an angular.element().find() call. What can go here depends on whether you have full jQuery loaded or just jQLite with angularjs. // div is only used as div.ta-insert-video caught in filter. -.value('taSelectableElements', ['a','img']) + .value('taSelectableElements', ['a','img']) // This is an array of objects with the following options: // selector: a jqLite or jQuery selector string // customAttribute: an attribute to search for // renderLogic: // Both or one of selector and customAttribute must be defined. -.value('taCustomRenderers', [ - { - // Parse back out: '
' - // To correct video element. For now only support youtube - selector: 'img', - customAttribute: 'ta-insert-video', - renderLogic: function(element){ - var iframe = angular.element(''); - var attributes = element.prop("attributes"); - // loop through element attributes and apply them on iframe - angular.forEach(attributes, function(attr) { - iframe.attr(attr.name, attr.value); - }); - iframe.attr('src', iframe.attr('ta-insert-video')); - element.replaceWith(iframe); + .value('taCustomRenderers', [ + { + // Parse back out: '
' + // To correct video element. For now only support youtube + selector: 'img', + customAttribute: 'ta-insert-video', + renderLogic: function(element){ + var iframe = angular.element(''); + var attributes = element.prop("attributes"); + // loop through element attributes and apply them on iframe + angular.forEach(attributes, function(attr) { + iframe.attr(attr.name, attr.value); + }); + iframe.attr('src', iframe.attr('ta-insert-video')); + element.replaceWith(iframe); + } } - } -]) + ]) -.value('taTranslations', { - // moved to sub-elements - //toggleHTML: "Toggle HTML", - //insertImage: "Please enter a image URL to insert", - //insertLink: "Please enter a URL to insert", - //insertVideo: "Please enter a youtube URL to embed", - html: { - tooltip: 'Toggle html / Rich Text' - }, - // tooltip for heading - might be worth splitting - heading: { - tooltip: 'Heading ' - }, - p: { - tooltip: 'Paragraph' - }, - pre: { - tooltip: 'Preformatted text' - }, - ul: { - tooltip: 'Unordered List' - }, - ol: { - tooltip: 'Ordered List' - }, - quote: { - tooltip: 'Quote/unqoute selection or paragraph' - }, - undo: { - tooltip: 'Undo' - }, - redo: { - tooltip: 'Redo' - }, - bold: { - tooltip: 'Bold' - }, - italic: { - tooltip: 'Italic' - }, - underline: { - tooltip: 'Underline' - }, - strikeThrough:{ - tooltip: 'Strikethrough' - }, - justifyLeft: { - tooltip: 'Align text left' - }, - justifyRight: { - tooltip: 'Align text right' - }, - justifyCenter: { - tooltip: 'Center' - }, - indent: { - tooltip: 'Increase indent' - }, - outdent: { - tooltip: 'Decrease indent' - }, - clear: { - tooltip: 'Clear formatting' - }, - insertImage: { - dialogPrompt: 'Please enter an image URL to insert', - tooltip: 'Insert image', - hotkey: 'the - possibly language dependent hotkey ... for some future implementation' - }, - insertVideo: { - tooltip: 'Insert video', - dialogPrompt: 'Please enter a youtube URL to embed' - }, - insertLink: { - tooltip: 'Insert / edit link', - dialogPrompt: "Please enter a URL to insert" - }, - editLink: { - reLinkButton: { - tooltip: "Relink" + .value('taTranslations', { + // moved to sub-elements + //toggleHTML: "Toggle HTML", + //insertImage: "Please enter a image URL to insert", + //insertLink: "Please enter a URL to insert", + //insertVideo: "Please enter a youtube URL to embed", + html: { + tooltip: 'Toggle html / Rich Text' }, - unLinkButton: { - tooltip: "Unlink" + // tooltip for heading - might be worth splitting + heading: { + tooltip: 'Heading ' }, - targetToggle: { - buttontext: "Open in New Window" - } - }, - wordcount: { - tooltip: 'Display words Count' - }, - charcount: { - tooltip: 'Display characters Count' - } -}) -.run(['taRegisterTool', '$window', 'taTranslations', 'taSelection', function(taRegisterTool, $window, taTranslations, taSelection){ - taRegisterTool("html", { - iconclass: 'fa fa-code', - tooltiptext: taTranslations.html.tooltip, - action: function(){ - this.$editor().switchView(); + p: { + tooltip: 'Paragraph' }, - activeState: function(){ - return this.$editor().showHtml; - } - }); - // add the Header tools - // convenience functions so that the loop works correctly - var _retActiveStateFunction = function(q){ - return function(){ return this.$editor().queryFormatBlockState(q); }; - }; - var headerAction = function(){ - return this.$editor().wrapSelection("formatBlock", "<" + this.name.toUpperCase() +">"); - }; - angular.forEach(['h1','h2','h3','h4','h5','h6'], function(h){ - taRegisterTool(h.toLowerCase(), { - buttontext: h.toUpperCase(), - tooltiptext: taTranslations.heading.tooltip + h.charAt(1), - action: headerAction, - activeState: _retActiveStateFunction(h.toLowerCase()) - }); - }); - taRegisterTool('p', { - buttontext: 'P', - tooltiptext: taTranslations.p.tooltip, - action: function(){ - return this.$editor().wrapSelection("formatBlock", "

"); + pre: { + tooltip: 'Preformatted text' }, - activeState: function(){ return this.$editor().queryFormatBlockState('p'); } - }); - // key: pre -> taTranslations[key].tooltip, taTranslations[key].buttontext - taRegisterTool('pre', { - buttontext: 'pre', - tooltiptext: taTranslations.pre.tooltip, - action: function(){ - return this.$editor().wrapSelection("formatBlock", "

");
+		ul: {
+			tooltip: 'Unordered List'
 		},
-		activeState: function(){ return this.$editor().queryFormatBlockState('pre'); }
-	});
-	taRegisterTool('ul', {
-		iconclass: 'fa fa-list-ul',
-		tooltiptext: taTranslations.ul.tooltip,
-		action: function(){
-			return this.$editor().wrapSelection("insertUnorderedList", null);
+		ol: {
+			tooltip: 'Ordered List'
 		},
-		activeState: function(){ return this.$editor().queryCommandState('insertUnorderedList'); }
-	});
-	taRegisterTool('ol', {
-		iconclass: 'fa fa-list-ol',
-		tooltiptext: taTranslations.ol.tooltip,
-		action: function(){
-			return this.$editor().wrapSelection("insertOrderedList", null);
+		quote: {
+			tooltip: 'Quote/unqoute selection or paragraph'
 		},
-		activeState: function(){ return this.$editor().queryCommandState('insertOrderedList'); }
-	});
-	taRegisterTool('quote', {
-		iconclass: 'fa fa-quote-right',
-		tooltiptext: taTranslations.quote.tooltip,
-		action: function(){
-			return this.$editor().wrapSelection("formatBlock", "
"); + undo: { + tooltip: 'Undo' }, - activeState: function(){ return this.$editor().queryFormatBlockState('blockquote'); } - }); - taRegisterTool('undo', { - iconclass: 'fa fa-undo', - tooltiptext: taTranslations.undo.tooltip, - action: function(){ - return this.$editor().wrapSelection("undo", null); - } - }); - taRegisterTool('redo', { - iconclass: 'fa fa-repeat', - tooltiptext: taTranslations.redo.tooltip, - action: function(){ - return this.$editor().wrapSelection("redo", null); - } - }); - taRegisterTool('bold', { - iconclass: 'fa fa-bold', - tooltiptext: taTranslations.bold.tooltip, - action: function(){ - return this.$editor().wrapSelection("bold", null); + redo: { + tooltip: 'Redo' }, - activeState: function(){ - return this.$editor().queryCommandState('bold'); + bold: { + tooltip: 'Bold' }, - commandKeyCode: 98 - }); - taRegisterTool('justifyLeft', { - iconclass: 'fa fa-align-left', - tooltiptext: taTranslations.justifyLeft.tooltip, - action: function(){ - return this.$editor().wrapSelection("justifyLeft", null); + italic: { + tooltip: 'Italic' }, - activeState: function(commonElement){ - var result = false; - if(commonElement) result = - commonElement.css('text-align') === 'left' || - commonElement.attr('align') === 'left' || - ( - commonElement.css('text-align') !== 'right' && - commonElement.css('text-align') !== 'center' && - commonElement.css('text-align') !== 'justify' && - !this.$editor().queryCommandState('justifyRight') && - !this.$editor().queryCommandState('justifyCenter') - ) && !this.$editor().queryCommandState('justifyFull'); - result = result || this.$editor().queryCommandState('justifyLeft'); - return result; - } - }); - taRegisterTool('justifyRight', { - iconclass: 'fa fa-align-right', - tooltiptext: taTranslations.justifyRight.tooltip, - action: function(){ - return this.$editor().wrapSelection("justifyRight", null); + underline: { + tooltip: 'Underline' }, - activeState: function(commonElement){ - var result = false; - if(commonElement) result = commonElement.css('text-align') === 'right'; - result = result || this.$editor().queryCommandState('justifyRight'); - return result; - } - }); - taRegisterTool('justifyCenter', { - iconclass: 'fa fa-align-center', - tooltiptext: taTranslations.justifyCenter.tooltip, - action: function(){ - return this.$editor().wrapSelection("justifyCenter", null); + strikeThrough:{ + tooltip: 'Strikethrough' }, - activeState: function(commonElement){ - var result = false; - if(commonElement) result = commonElement.css('text-align') === 'center'; - result = result || this.$editor().queryCommandState('justifyCenter'); - return result; - } - }); - taRegisterTool('indent', { - iconclass: 'fa fa-indent', - tooltiptext: taTranslations.indent.tooltip, - action: function(){ - return this.$editor().wrapSelection("indent", null); + justifyLeft: { + tooltip: 'Align text left' }, - activeState: function(){ - return this.$editor().queryFormatBlockState('blockquote'); - } - }); - taRegisterTool('outdent', { - iconclass: 'fa fa-outdent', - tooltiptext: taTranslations.outdent.tooltip, - action: function(){ - return this.$editor().wrapSelection("outdent", null); + justifyRight: { + tooltip: 'Align text right' }, - activeState: function(){ - return false; - } - }); - taRegisterTool('italics', { - iconclass: 'fa fa-italic', - tooltiptext: taTranslations.italic.tooltip, - action: function(){ - return this.$editor().wrapSelection("italic", null); + justifyCenter: { + tooltip: 'Center' }, - activeState: function(){ - return this.$editor().queryCommandState('italic'); + indent: { + tooltip: 'Increase indent' }, - commandKeyCode: 105 - }); - taRegisterTool('underline', { - iconclass: 'fa fa-underline', - tooltiptext: taTranslations.underline.tooltip, - action: function(){ - return this.$editor().wrapSelection("underline", null); + outdent: { + tooltip: 'Decrease indent' }, - activeState: function(){ - return this.$editor().queryCommandState('underline'); + clear: { + tooltip: 'Clear formatting' }, - commandKeyCode: 117 - }); - taRegisterTool('strikeThrough', { - iconclass: 'fa fa-strikethrough', - action: function(){ - return this.$editor().wrapSelection("strikeThrough", null); + insertImage: { + dialogPrompt: 'Please enter an image URL to insert', + tooltip: 'Insert image', + hotkey: 'the - possibly language dependent hotkey ... for some future implementation' }, - activeState: function(){ - return document.queryCommandState('strikeThrough'); - } - }); - taRegisterTool('clear', { - iconclass: 'fa fa-ban', - tooltiptext: taTranslations.clear.tooltip, - action: function(deferred, restoreSelection){ - var i; - this.$editor().wrapSelection("removeFormat", null); - var possibleNodes = angular.element(taSelection.getSelectionElement()); - // remove lists - var removeListElements = function(list){ - list = angular.element(list); - var prevElement = list; - angular.forEach(list.children(), function(liElem){ - var newElem = angular.element('

'); - newElem.html(angular.element(liElem).html()); - prevElement.after(newElem); - prevElement = newElem; - }); - list.remove(); - }; - angular.forEach(possibleNodes.find("ul"), removeListElements); - angular.forEach(possibleNodes.find("ol"), removeListElements); - if(possibleNodes[0].tagName.toLowerCase() === 'li'){ - var _list = possibleNodes[0].parentNode.childNodes; - var _preLis = [], _postLis = [], _found = false; - for(i = 0; i < _list.length; i++){ - if(_list[i] === possibleNodes[0]){ - _found = true; - }else if(!_found) _preLis.push(_list[i]); - else _postLis.push(_list[i]); - } - var _parent = angular.element(possibleNodes[0].parentNode); - var newElem = angular.element('

'); - newElem.html(angular.element(possibleNodes[0]).html()); - if(_preLis.length === 0 || _postLis.length === 0){ - if(_postLis.length === 0) _parent.after(newElem); - else _parent[0].parentNode.insertBefore(newElem[0], _parent[0]); - - if(_preLis.length === 0 && _postLis.length === 0) _parent.remove(); - else angular.element(possibleNodes[0]).remove(); - }else{ - var _firstList = angular.element('<'+_parent[0].tagName+'>'); - var _secondList = angular.element('<'+_parent[0].tagName+'>'); - for(i = 0; i < _preLis.length; i++) _firstList.append(angular.element(_preLis[i])); - for(i = 0; i < _postLis.length; i++) _secondList.append(angular.element(_postLis[i])); - _parent.after(_secondList); - _parent.after(newElem); - _parent.after(_firstList); - _parent.remove(); - } - taSelection.setSelectionToElementEnd(newElem[0]); + insertVideo: { + tooltip: 'Insert video', + dialogPrompt: 'Please enter a youtube URL to embed' + }, + insertLink: { + tooltip: 'Insert / edit link', + dialogPrompt: "Please enter a URL to insert" + }, + editLink: { + reLinkButton: { + tooltip: "Relink" + }, + unLinkButton: { + tooltip: "Unlink" + }, + targetToggle: { + buttontext: "Open in New Window" } - // clear out all class attributes. These do not seem to be cleared via removeFormat - var $editor = this.$editor(); - var recursiveRemoveClass = function(node){ - node = angular.element(node); - if(node[0] !== $editor.displayElements.text[0]) node.removeAttr('class'); - angular.forEach(node.children(), recursiveRemoveClass); - }; - angular.forEach(possibleNodes, recursiveRemoveClass); - // check if in list. If not in list then use formatBlock option - if(possibleNodes[0].tagName.toLowerCase() !== 'li' && - possibleNodes[0].tagName.toLowerCase() !== 'ol' && - possibleNodes[0].tagName.toLowerCase() !== 'ul') this.$editor().wrapSelection("formatBlock", "default"); - restoreSelection(); + }, + wordcount: { + tooltip: 'Display words Count' + }, + charcount: { + tooltip: 'Display characters Count' } - }); - - var imgOnSelectAction = function(event, $element, editorScope){ - // setup the editor toolbar - // Credit to the work at http://hackerwins.github.io/summernote/ for this editbar logic/display - var finishEdit = function(){ - editorScope.updateTaBindtaTextElement(); - editorScope.hidePopover(); + }) + .run(['taRegisterTool', '$window', 'taTranslations', 'taSelection', function(taRegisterTool, $window, taTranslations, taSelection){ + taRegisterTool("html", { + iconclass: 'fa fa-code', + tooltiptext: taTranslations.html.tooltip, + action: function(){ + this.$editor().switchView(); + }, + activeState: function(){ + return this.$editor().showHtml; + } + }); + // add the Header tools + // convenience functions so that the loop works correctly + var _retActiveStateFunction = function(q){ + return function(){ return this.$editor().queryFormatBlockState(q); }; }; - event.preventDefault(); - editorScope.displayElements.popover.css('width', '375px'); - var container = editorScope.displayElements.popoverContainer; - container.empty(); - var buttonGroup = angular.element('
'); - var fullButton = angular.element(''); - fullButton.on('click', function(event){ - event.preventDefault(); - $element.css({ - 'width': '100%', - 'height': '' + var headerAction = function(){ + var block = this.name.charAt(0) + (parseInt(this.name.charAt(1)) + 1); + + return this.$editor().wrapSelection("formatBlock", "<" + block.toUpperCase() +">"); + }; + angular.forEach(['h1','h2','h3','h4','h5'], function(h){ + taRegisterTool(h.toLowerCase(), { + buttontext: h.toUpperCase(), + tooltiptext: taTranslations.heading.tooltip + h.charAt(1), + action: headerAction, + activeState: _retActiveStateFunction(h.toLowerCase()) }); - finishEdit(); }); - var halfButton = angular.element(''); - halfButton.on('click', function(event){ - event.preventDefault(); - $element.css({ - 'width': '50%', - 'height': '' - }); - finishEdit(); + taRegisterTool('p', { + buttontext: 'P', + tooltiptext: taTranslations.p.tooltip, + action: function(){ + return this.$editor().wrapSelection("formatBlock", "

"); + }, + activeState: function(){ return this.$editor().queryFormatBlockState('p'); } }); - var quartButton = angular.element(''); - quartButton.on('click', function(event){ - event.preventDefault(); - $element.css({ - 'width': '25%', - 'height': '' - }); - finishEdit(); + // key: pre -> taTranslations[key].tooltip, taTranslations[key].buttontext + taRegisterTool('pre', { + buttontext: 'pre', + tooltiptext: taTranslations.pre.tooltip, + action: function(){ + return this.$editor().wrapSelection("formatBlock", "

");
+			},
+			activeState: function(){ return this.$editor().queryFormatBlockState('pre'); }
 		});
-		var resetButton = angular.element('');
-		resetButton.on('click', function(event){
-			event.preventDefault();
-			$element.css({
-				width: '',
-				height: ''
-			});
-			finishEdit();
+		taRegisterTool('ul', {
+			iconclass: 'fa fa-list-ul',
+			tooltiptext: taTranslations.ul.tooltip,
+			action: function(){
+				return this.$editor().wrapSelection("insertUnorderedList", null);
+			},
+			activeState: function(){ return this.$editor().queryCommandState('insertUnorderedList'); }
 		});
-		buttonGroup.append(fullButton);
-		buttonGroup.append(halfButton);
-		buttonGroup.append(quartButton);
-		buttonGroup.append(resetButton);
-		container.append(buttonGroup);
-		
-		buttonGroup = angular.element('
'); - var floatLeft = angular.element(''); - floatLeft.on('click', function(event){ - event.preventDefault(); - // webkit - $element.css('float', 'left'); - // firefox - $element.css('cssFloat', 'left'); - // IE < 8 - $element.css('styleFloat', 'left'); - finishEdit(); + taRegisterTool('ol', { + iconclass: 'fa fa-list-ol', + tooltiptext: taTranslations.ol.tooltip, + action: function(){ + return this.$editor().wrapSelection("insertOrderedList", null); + }, + activeState: function(){ return this.$editor().queryCommandState('insertOrderedList'); } }); - var floatRight = angular.element(''); - floatRight.on('click', function(event){ - event.preventDefault(); - // webkit - $element.css('float', 'right'); - // firefox - $element.css('cssFloat', 'right'); - // IE < 8 - $element.css('styleFloat', 'right'); - finishEdit(); + taRegisterTool('quote', { + iconclass: 'fa fa-quote-right', + tooltiptext: taTranslations.quote.tooltip, + action: function(){ + return this.$editor().wrapSelection("formatBlock", "
"); + }, + activeState: function(){ return this.$editor().queryFormatBlockState('blockquote'); } }); - var floatNone = angular.element(''); - floatNone.on('click', function(event){ - event.preventDefault(); - // webkit - $element.css('float', ''); - // firefox - $element.css('cssFloat', ''); - // IE < 8 - $element.css('styleFloat', ''); - finishEdit(); + taRegisterTool('undo', { + iconclass: 'fa fa-undo', + tooltiptext: taTranslations.undo.tooltip, + action: function(){ + return this.$editor().wrapSelection("undo", null); + } }); - buttonGroup.append(floatLeft); - buttonGroup.append(floatNone); - buttonGroup.append(floatRight); - container.append(buttonGroup); - - buttonGroup = angular.element('
'); - var remove = angular.element(''); - remove.on('click', function(event){ - event.preventDefault(); - $element.remove(); - finishEdit(); + taRegisterTool('redo', { + iconclass: 'fa fa-repeat', + tooltiptext: taTranslations.redo.tooltip, + action: function(){ + return this.$editor().wrapSelection("redo", null); + } + }); + taRegisterTool('bold', { + iconclass: 'fa fa-bold', + tooltiptext: taTranslations.bold.tooltip, + action: function(){ + return this.$editor().wrapSelection("bold", null); + }, + activeState: function(){ + return this.$editor().queryCommandState('bold'); + }, + commandKeyCode: 98 }); - buttonGroup.append(remove); - container.append(buttonGroup); - - editorScope.showPopover($element); - editorScope.showResizeOverlay($element); - }; - - taRegisterTool('insertImage', { - iconclass: 'fa fa-picture-o', - tooltiptext: taTranslations.insertImage.tooltip, - action: function(){ - var imageLink; - imageLink = $window.prompt(taTranslations.insertImage.dialogPrompt, 'http://'); - if(imageLink && imageLink !== '' && imageLink !== 'http://'){ - return this.$editor().wrapSelection('insertImage', imageLink, true); + taRegisterTool('justifyLeft', { + iconclass: 'fa fa-align-left', + tooltiptext: taTranslations.justifyLeft.tooltip, + action: function(){ + return this.$editor().addCssToSelection("wysiwyg-justify-left", ['wysiwyg-justify-center', 'wysiwyg-justify-right']); + }, + activeState: function(commonElement){ + return this.$editor().selectedHasClass("wysiwyg-justify-left", true); + } - }, - onElementSelect: { - element: 'img', - action: imgOnSelectAction - } - }); - taRegisterTool('insertVideo', { - iconclass: 'fa fa-youtube-play', - tooltiptext: taTranslations.insertVideo.tooltip, - action: function(){ - var urlPrompt; - urlPrompt = $window.prompt(taTranslations.insertVideo.dialogPrompt, 'https://'); - if (urlPrompt && urlPrompt !== '' && urlPrompt !== 'https://') { - // get the video ID - var ids = urlPrompt.match(/(\?|&)v=[^&]*/); - /* istanbul ignore else: if it's invalid don't worry - though probably should show some kind of error message */ - if(ids && ids.length > 0){ - // create the embed link - var urlLink = "https://www.youtube.com/embed/" + ids[0].substring(3); - // create the HTML - // for all options see: http://stackoverflow.com/questions/2068344/how-do-i-get-a-youtube-video-thumbnail-from-the-youtube-api - // maxresdefault.jpg seems to be undefined on some. - var embed = ''; - // insert - return this.$editor().wrapSelection('insertHTML', embed, true); - } + }); + taRegisterTool('justifyRight', { + iconclass: 'fa fa-align-right', + tooltiptext: taTranslations.justifyRight.tooltip, + action: function(){ + return this.$editor().addCssToSelection("wysiwyg-justify-right", ['wysiwyg-justify-center', 'wysiwyg-justify-left']); + + }, + activeState: function(){ + return this.$editor().selectedHasClass("wysiwyg-justify-right"); + } - }, - onElementSelect: { - element: 'img', - onlyWithAttrs: ['ta-insert-video'], - action: imgOnSelectAction - } - }); - taRegisterTool('insertLink', { - tooltiptext: taTranslations.insertLink.tooltip, - iconclass: 'fa fa-link', - action: function(){ - var urlLink; - urlLink = $window.prompt(taTranslations.insertLink.dialogPrompt, 'http://'); - if(urlLink && urlLink !== '' && urlLink !== 'http://'){ - return this.$editor().wrapSelection('createLink', urlLink, true); + }); + taRegisterTool('justifyCenter', { + iconclass: 'fa fa-align-center', + tooltiptext: taTranslations.justifyCenter.tooltip, + action: function(){ + return this.$editor().addCssToSelection("wysiwyg-justify-center", ['wysiwyg-justify-right', 'wysiwyg-justify-left']); + }, + activeState: function(){ + return this.$editor().selectedHasClass("wysiwyg-justify-center"); + } - }, - activeState: function(commonElement){ - if(commonElement) return commonElement[0].tagName === 'A'; - return false; - }, - onElementSelect: { - element: 'a', - action: function(event, $element, editorScope){ - // setup the editor toolbar - // Credit to the work at http://hackerwins.github.io/summernote/ for this editbar logic + }); + taRegisterTool('indent', { + iconclass: 'fa fa-indent', + tooltiptext: taTranslations.indent.tooltip, + action: function(){ + return this.$editor().wrapSelection("indent", null); + }, + activeState: function(){ + return this.$editor().queryFormatBlockState('blockquote'); + } + }); + taRegisterTool('outdent', { + iconclass: 'fa fa-outdent', + tooltiptext: taTranslations.outdent.tooltip, + action: function(){ + return this.$editor().wrapSelection("outdent", null); + }, + activeState: function(){ + return false; + } + }); + taRegisterTool('italics', { + iconclass: 'fa fa-italic', + tooltiptext: taTranslations.italic.tooltip, + action: function(){ + return this.$editor().wrapSelection("italic", null); + }, + activeState: function(){ + return this.$editor().queryCommandState('italic'); + }, + commandKeyCode: 105 + }); + taRegisterTool('underline', { + iconclass: 'fa fa-underline', + tooltiptext: taTranslations.underline.tooltip, + action: function(){ + return this.$editor().wrapSelection("underline", null); + }, + activeState: function(){ + return this.$editor().queryCommandState('underline'); + }, + commandKeyCode: 117 + }); + taRegisterTool('strikeThrough', { + iconclass: 'fa fa-strikethrough', + action: function(){ + return this.$editor().wrapSelection("strikeThrough"); + }, + activeState: function(){ + return document.queryCommandState('strikeThrough'); + } + }); + taRegisterTool('clear', { + iconclass: 'fa fa-ban', + tooltiptext: taTranslations.clear.tooltip, + action: function(deferred, restoreSelection){ + var i; + this.$editor().wrapSelection("removeFormat", null); + var possibleNodes = angular.element(taSelection.getSelectionElement()); + // remove lists + var removeListElements = function(list){ + list = angular.element(list); + var prevElement = list; + angular.forEach(list.children(), function(liElem){ + var newElem = angular.element('

'); + newElem.html(angular.element(liElem).html()); + prevElement.after(newElem); + prevElement = newElem; + }); + list.remove(); + }; + angular.forEach(possibleNodes.find("ul"), removeListElements); + angular.forEach(possibleNodes.find("ol"), removeListElements); + if(possibleNodes[0].tagName.toLowerCase() === 'li'){ + var _list = possibleNodes[0].parentNode.childNodes; + var _preLis = [], _postLis = [], _found = false; + for(i = 0; i < _list.length; i++){ + if(_list[i] === possibleNodes[0]){ + _found = true; + }else if(!_found) _preLis.push(_list[i]); + else _postLis.push(_list[i]); + } + var _parent = angular.element(possibleNodes[0].parentNode); + var newElem = angular.element('

'); + newElem.html(angular.element(possibleNodes[0]).html()); + if(_preLis.length === 0 || _postLis.length === 0){ + if(_postLis.length === 0) _parent.after(newElem); + else _parent[0].parentNode.insertBefore(newElem[0], _parent[0]); + + if(_preLis.length === 0 && _postLis.length === 0) _parent.remove(); + else angular.element(possibleNodes[0]).remove(); + }else{ + var _firstList = angular.element('<'+_parent[0].tagName+'>'); + var _secondList = angular.element('<'+_parent[0].tagName+'>'); + for(i = 0; i < _preLis.length; i++) _firstList.append(angular.element(_preLis[i])); + for(i = 0; i < _postLis.length; i++) _secondList.append(angular.element(_postLis[i])); + _parent.after(_secondList); + _parent.after(newElem); + _parent.after(_firstList); + _parent.remove(); + } + taSelection.setSelectionToElementEnd(newElem[0]); + } + // clear out all class attributes. These do not seem to be cleared via removeFormat + var $editor = this.$editor(); + var recursiveRemoveClass = function(node){ + node = angular.element(node); + if(node[0] !== $editor.displayElements.text[0]) node.removeAttr('class'); + angular.forEach(node.children(), recursiveRemoveClass); + }; + angular.forEach(possibleNodes, recursiveRemoveClass); + // check if in list. If not in list then use formatBlock option + if(possibleNodes[0].tagName.toLowerCase() !== 'li' && + possibleNodes[0].tagName.toLowerCase() !== 'ol' && + possibleNodes[0].tagName.toLowerCase() !== 'ul') this.$editor().wrapSelection("formatBlock", "default"); + restoreSelection(); + } + }); + + var imgOnSelectAction = function(event, $element, editorScope){ + // setup the editor toolbar + // Credit to the work at http://hackerwins.github.io/summernote/ for this editbar logic/display + var finishEdit = function(){ + editorScope.updateTaBindtaTextElement(); + editorScope.hidePopover(); + }; + event.preventDefault(); + editorScope.displayElements.popover.css('width', '375px'); + var container = editorScope.displayElements.popoverContainer; + container.empty(); + var buttonGroup = angular.element('
'); + var fullButton = angular.element(''); + fullButton.on('click', function(event){ event.preventDefault(); - editorScope.displayElements.popover.css('width', '435px'); - var container = editorScope.displayElements.popoverContainer; - container.empty(); - container.css('line-height', '28px'); - var link = angular.element('' + $element.attr('href') + ''); - link.css({ - 'display': 'inline-block', - 'max-width': '200px', - 'overflow': 'hidden', - 'text-overflow': 'ellipsis', - 'white-space': 'nowrap', - 'vertical-align': 'middle' + $element.css({ + 'width': '100%', + 'height': '' }); - container.append(link); - var buttonGroup = angular.element('
'); - var reLinkButton = angular.element(''); - reLinkButton.on('click', function(event){ - event.preventDefault(); - var urlLink = $window.prompt(taTranslations.insertLink.dialogPrompt, $element.attr('href')); - if(urlLink && urlLink !== '' && urlLink !== 'http://'){ - $element.attr('href', urlLink); - editorScope.updateTaBindtaTextElement(); - } - editorScope.hidePopover(); + finishEdit(); + }); + var halfButton = angular.element(''); + halfButton.on('click', function(event){ + event.preventDefault(); + $element.css({ + 'width': '50%', + 'height': '' }); - buttonGroup.append(reLinkButton); - var unLinkButton = angular.element(''); - // directly before this click event is fired a digest is fired off whereby the reference to $element is orphaned off - unLinkButton.on('click', function(event){ - event.preventDefault(); - $element.replaceWith($element.contents()); - editorScope.updateTaBindtaTextElement(); - editorScope.hidePopover(); + finishEdit(); + }); + var quartButton = angular.element(''); + quartButton.on('click', function(event){ + event.preventDefault(); + $element.css({ + 'width': '25%', + 'height': '' + }); + finishEdit(); + }); + var resetButton = angular.element(''); + resetButton.on('click', function(event){ + event.preventDefault(); + $element.css({ + width: '', + height: '' }); - buttonGroup.append(unLinkButton); - var targetToggle = angular.element(''); - if($element.attr('target') === '_blank'){ - targetToggle.addClass('active'); + finishEdit(); + }); + buttonGroup.append(fullButton); + buttonGroup.append(halfButton); + buttonGroup.append(quartButton); + buttonGroup.append(resetButton); + container.append(buttonGroup); + + buttonGroup = angular.element('
'); + var floatLeft = angular.element(''); + floatLeft.on('click', function(event){ + event.preventDefault(); + // webkit + $element.css('float', 'left'); + // firefox + $element.css('cssFloat', 'left'); + // IE < 8 + $element.css('styleFloat', 'left'); + finishEdit(); + }); + var floatRight = angular.element(''); + floatRight.on('click', function(event){ + event.preventDefault(); + // webkit + $element.css('float', 'right'); + // firefox + $element.css('cssFloat', 'right'); + // IE < 8 + $element.css('styleFloat', 'right'); + finishEdit(); + }); + var floatNone = angular.element(''); + floatNone.on('click', function(event){ + event.preventDefault(); + // webkit + $element.css('float', ''); + // firefox + $element.css('cssFloat', ''); + // IE < 8 + $element.css('styleFloat', ''); + finishEdit(); + }); + buttonGroup.append(floatLeft); + buttonGroup.append(floatNone); + buttonGroup.append(floatRight); + container.append(buttonGroup); + + buttonGroup = angular.element('
'); + var remove = angular.element(''); + remove.on('click', function(event){ + event.preventDefault(); + $element.remove(); + finishEdit(); + }); + buttonGroup.append(remove); + container.append(buttonGroup); + + editorScope.showPopover($element); + editorScope.showResizeOverlay($element); + }; + + taRegisterTool('insertImage', { + iconclass: 'fa fa-picture-o', + tooltiptext: taTranslations.insertImage.tooltip, + action: function(){ + var imageLink; + imageLink = $window.prompt(taTranslations.insertImage.dialogPrompt, 'http://'); + if(imageLink && imageLink !== '' && imageLink !== 'http://'){ + return this.$editor().wrapSelection('insertImage', imageLink, true); + } + }, + onElementSelect: { + element: 'img', + action: imgOnSelectAction + } + }); + taRegisterTool('insertVideo', { + iconclass: 'fa fa-youtube-play', + tooltiptext: taTranslations.insertVideo.tooltip, + action: function(){ + var urlPrompt; + urlPrompt = $window.prompt(taTranslations.insertVideo.dialogPrompt, 'https://'); + if (urlPrompt && urlPrompt !== '' && urlPrompt !== 'https://') { + // get the video ID + var ids = urlPrompt.match(/(\?|&)v=[^&]*/); + /* istanbul ignore else: if it's invalid don't worry - though probably should show some kind of error message */ + if(ids && ids.length > 0){ + // create the embed link + var urlLink = "https://www.youtube.com/embed/" + ids[0].substring(3); + // create the HTML + // for all options see: http://stackoverflow.com/questions/2068344/how-do-i-get-a-youtube-video-thumbnail-from-the-youtube-api + // maxresdefault.jpg seems to be undefined on some. + var embed = ''; + // insert + return this.$editor().wrapSelection('insertHTML', embed, true); + } + } + }, + onElementSelect: { + element: 'img', + onlyWithAttrs: ['ta-insert-video'], + action: imgOnSelectAction + } + }); + taRegisterTool('insertLink', { + tooltiptext: taTranslations.insertLink.tooltip, + iconclass: 'fa fa-link', + action: function(){ + var urlLink; + urlLink = $window.prompt(taTranslations.insertLink.dialogPrompt, 'http://'); + if(urlLink && urlLink !== '' && urlLink !== 'http://'){ + return this.$editor().wrapSelection('createLink', urlLink, true); } - targetToggle.on('click', function(event){ + }, + activeState: function(commonElement){ + if(commonElement) return commonElement[0].tagName === 'A'; + return false; + }, + onElementSelect: { + element: 'a', + action: function(event, $element, editorScope){ + // setup the editor toolbar + // Credit to the work at http://hackerwins.github.io/summernote/ for this editbar logic event.preventDefault(); - $element.attr('target', ($element.attr('target') === '_blank') ? '' : '_blank'); - targetToggle.toggleClass('active'); - editorScope.updateTaBindtaTextElement(); - }); - buttonGroup.append(targetToggle); - container.append(buttonGroup); - editorScope.showPopover($element); + editorScope.displayElements.popover.css('width', '435px'); + var container = editorScope.displayElements.popoverContainer; + container.empty(); + container.css('line-height', '28px'); + var link = angular.element('' + $element.attr('href') + ''); + link.css({ + 'display': 'inline-block', + 'max-width': '200px', + 'overflow': 'hidden', + 'text-overflow': 'ellipsis', + 'white-space': 'nowrap', + 'vertical-align': 'middle' + }); + container.append(link); + var buttonGroup = angular.element('
'); + var reLinkButton = angular.element(''); + reLinkButton.on('click', function(event){ + event.preventDefault(); + var urlLink = $window.prompt(taTranslations.insertLink.dialogPrompt, $element.attr('href')); + if(urlLink && urlLink !== '' && urlLink !== 'http://'){ + $element.attr('href', urlLink); + editorScope.updateTaBindtaTextElement(); + } + editorScope.hidePopover(); + }); + buttonGroup.append(reLinkButton); + var unLinkButton = angular.element(''); + // directly before this click event is fired a digest is fired off whereby the reference to $element is orphaned off + unLinkButton.on('click', function(event){ + event.preventDefault(); + $element.replaceWith($element.contents()); + editorScope.updateTaBindtaTextElement(); + editorScope.hidePopover(); + }); + buttonGroup.append(unLinkButton); + var targetToggle = angular.element(''); + if($element.attr('target') === '_blank'){ + targetToggle.addClass('active'); + } + targetToggle.on('click', function(event){ + event.preventDefault(); + $element.attr('target', ($element.attr('target') === '_blank') ? '' : '_blank'); + targetToggle.toggleClass('active'); + editorScope.updateTaBindtaTextElement(); + }); + buttonGroup.append(targetToggle); + container.append(buttonGroup); + editorScope.showPopover($element); + } } - } - }); - taRegisterTool('wordcount', { - display: '
Words:{{wordcount}}
', - disabled: true, - wordcount: 0, - activeState: function(){ // this fires on keyup - var textElement = this.$editor().displayElements.text; - var workingHTML = textElement[0].innerHTML; - var sourceText = workingHTML.replace(/(<[^>]*?>)/ig, ' '); // replace all html tags with spaces - - // Caculate number of words - var sourceTextMatches = sourceText.match(/\S+/g); - var noOfWords = sourceTextMatches && sourceTextMatches.length || 0; - - //Set current scope - this.wordcount = noOfWords; - //Set editor scope - this.$editor().wordcount = noOfWords; - return false; - } - }); - taRegisterTool('charcount', { - display: '
Characters:{{charcount}}
', - disabled: true, - charcount: 0, - activeState: function(){ // this fires on keyup - var textElement = this.$editor().displayElements.text; - var sourceText = textElement[0].innerText || textElement[0].textContent; // to cover the non-jquery use case. - - // Caculate number of chars - var noOfChars = sourceText.replace(/(\r\n|\n|\r)/gm,"").replace(/^\s+/g,' ').replace(/\s+$/g, ' ').length; - //Set current scope - this.charcount = noOfChars; - //Set editor scope - this.$editor().charcount = noOfChars; - return false; - } - }); -}]); + }); + taRegisterTool('wordcount', { + display: '
Words:{{wordcount}}
', + disabled: true, + wordcount: 0, + activeState: function(){ // this fires on keyup + var textElement = this.$editor().displayElements.text; + var workingHTML = textElement[0].innerHTML; + var sourceText = workingHTML.replace(/(<[^>]*?>)/ig, ' '); // replace all html tags with spaces + + // Caculate number of words + var sourceTextMatches = sourceText.match(/\S+/g); + var noOfWords = sourceTextMatches && sourceTextMatches.length || 0; + + //Set current scope + this.wordcount = noOfWords; + //Set editor scope + this.$editor().wordcount = noOfWords; + return false; + } + }); + taRegisterTool('charcount', { + display: '
Characters:{{charcount}}
', + disabled: true, + charcount: 0, + activeState: function(){ // this fires on keyup + var textElement = this.$editor().displayElements.text; + var sourceText = textElement[0].innerText || textElement[0].textContent; // to cover the non-jquery use case. + + // Caculate number of chars + var noOfChars = sourceText.replace(/(\r\n|\n|\r)/gm,"").replace(/^\s+/g,' ').replace(/\s+$/g, ' ').length; + //Set current scope + this.charcount = noOfChars; + //Set editor scope + this.$editor().charcount = noOfChars; + return false; + } + }); + }]);