diff --git a/README.md b/README.md
index 1364091..24af457 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Demo
```
$ git clone https://github.com/TerryMooreII/angular-wysiwyg.git
-$ npm install
+$ npm install
$ gulp server
```
Open browser to http://localhost:4000/demo
@@ -28,7 +28,7 @@ Installation
Required dependancies
-----------------------
-* [AngularJS] (http://www.angularjs.com)
+* [AngularJS] (http://www.angularjs.com)
* [Font Awesome] (http://fortawesome.github.io/Font-Awesome/)
* [Twitter Bootstrap] (http://getbootstrap.com/2.3.2/)
* [bootstrap-color-picker] (https://github.com/buberdds/angular-bootstrap-colorpicker)
@@ -52,14 +52,14 @@ Options
Option|Description
---------------------|---------------
**ng-model** | REQUIRED - The angular data model
-**textarea-id** | The id to assign to the editable div
+**textarea-id** | REQUIRED (If using videos) - The id to assign to the editable div
**textarea-class** | The class(es) to assign to the the editable div
**textarea-height** | If the height is not specified in a text-area class then the hight of the editable div (default: 80px)
-**textarea-name** | The name attribute of the editable div
+**textarea-name** | The name attribute of the editable div
**textarea-required**| True/False HTML/AngularJS required validation
-**enable-bootstrap-title**| True/False whether or not to show the button hover title styled with bootstrap
+**enable-bootstrap-title**| True/False whether or not to show the button hover title styled with bootstrap
**textarea-menu** | Cusomize the wysiwyg buttons and button groups ***See Below** If nothing is specified then the default buttons and groups will be shown.
-**disabled** | Disable the buttons and wysiwig area
+**disabled** | Disable the buttons and wysiwig area
Buttons
--------------
@@ -69,7 +69,7 @@ If you don't need all of the buttons and functions of the default WYSIWYG editor
To do so you need to create a scope variable in your controller. This variable be an array that contains arrays of button groups.
```javascript
-
+
//This also happens to be the default menu options.
$scope.yourModel.customMenu = [
['bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript'],
@@ -81,25 +81,25 @@ To do so you need to create a scope variable in your controller. This variable
['ordered-list', 'unordered-list', 'outdent', 'indent'],
['left-justify', 'center-justify', 'right-justify'],
['code', 'quote', 'paragraph'],
- ['link', 'image']
+ ['link', 'image', 'video']
];
```
-So above each array will end up being a group of the specified buttons.
+So above each array will end up being a group of the specified buttons.
**Note:** The `font` and `font-size` dropdowns must be in thier own group.
-List of possible buttons |
-------------|
+List of possible buttons | Notes
+------------|--------------
bold |
-italic |
-underline |
-strikethrough |
+italic |
+underline |
+strikethrough |
subscript |
superscript |
-font |
+font |
font-size |
-font-color |
+font-color |
hilite-color |
remove-format |
ordered-list |
@@ -114,8 +114,4 @@ paragraph |
quote |
link |
image |
-
-
-
-
-
+video | Converts youtube and vimeo links to imbedded ones
diff --git a/demo/index.html b/demo/index.html
index 6b47fdb..84db74a 100644
--- a/demo/index.html
+++ b/demo/index.html
@@ -1,6 +1,6 @@
-
+
'
}
];
scope.formatBlock = scope.formatBlocks[0];
@@ -206,7 +207,12 @@ Requires:
function insertTab(html, position) {
var begining = html.substr(0, position);
var end = html.substr(position);
- return begining + '' + end;
+ var TAB_SPACES = 4;
+ var space;
+ for (var i = 0; i < TAB_SPACES; i++) {
+ space += ' ';
+ }
+ return begining + '' + space + '' + end;
}
function configureListeners() {
//Send message to calling controller that a button has been clicked.
@@ -216,20 +222,20 @@ Requires:
});
textarea.on('input keyup paste mouseup', function () {
var html = textarea.html();
- if (html == ' ') {
+ if (html === ' ') {
html = '';
}
ngModelController.$setViewValue(html);
});
textarea.on('keydown', function (event) {
- if (event.keyCode == 9) {
+ if (event.keyCode === 9) {
var TAB_SPACES = 4;
var html = textarea.html();
var selection = window.getSelection();
var position = selection.anchorOffset;
event.preventDefault(); // html = insertTab(html, position);
// textarea.html(html);
- // selection.collapse(textarea[0].firstChild, position + TAB_SPACES);
+ // selection.collapse(textarea[0].firstChild, position + TAB_SPACES);
}
});
textarea.on('click keyup focus mouseup', function () {
@@ -241,7 +247,7 @@ Requires:
scope.isSuperscript = itemIs('SUP');
//scope.cmdState('superscript');
scope.isSubscript = itemIs('SUB');
- //scope.cmdState('subscript');
+ //scope.cmdState('subscript');
scope.isRightJustified = scope.cmdState('justifyright');
scope.isLeftJustified = scope.cmdState('justifyleft');
scope.isCenterJustified = scope.cmdState('justifycenter');
@@ -313,7 +319,7 @@ Requires:
textarea.html(ngModelController.$viewValue);
};
scope.format = function (cmd, arg) {
- document.execCommand(cmd, false, arg);
+ document.execCommand(cmd, true, arg);
};
scope.cmdState = function (cmd) {
return document.queryCommandState(cmd);
@@ -331,6 +337,38 @@ Requires:
if (input && input !== undefined)
scope.format('insertimage', input);
};
+ //Finds the parent of a node with the specified id, or returns null
+ function findParent(node, id) {
+ if (node === null)
+ return null;
+ if (node.id === id)
+ return node;
+ return findParent(node.parentNode, id);
+ }
+ scope.insertVideo = function () {
+ var sel = document.getSelection();
+ if (sel.rangeCount <= 0) {
+ console.log('No range selected');
+ return;
+ }
+ var input = prompt('Enter the video URL');
+ if (input && input !== undefined) {
+ //Convertion of normal youtube and vimeo linkt into imbed links
+ var match;
+ if ((match = /youtube\.com\/watch\?v=(.*)/.exec(input)) !== null) {
+ input = 'https://www.youtube.com/embed/' + match[1];
+ } else if ((match = /[^player]\/vimeo\.com\/(.*)/.exec(input)) !== null) {
+ input = 'https://player.vimeo.com/video/' + match[1];
+ }
+ var videoElement = '';
+ var range = sel.getRangeAt(0);
+ //Prevent user from addin videos outside of the element
+ if (findParent(range.startContainer, scope.textareaId) === null)
+ return;
+ var nnode = range.createContextualFragment(videoElement);
+ range.insertNode(nnode);
+ }
+ };
scope.setFont = function () {
scope.format('fontname', scope.font);
};
@@ -380,6 +418,7 @@ Requires:
var el;
for (var i = 0; i < menu.length; i++) {
var menuGroup = create(getMenuGroup());
+ //console.log(menuGroup);
for (var j = 0; j < menu[i].length; j++) {
//link has two functions link and unlink
if (menu[i][j] === 'link') {
@@ -406,7 +445,7 @@ Requires:
}
if (obj.text && document.all) {
el.innerText = obj.text;
- } else {
+ } else if (obj.text) {
el.textContent = obj.text;
}
if (obj.classes) {
@@ -885,6 +924,28 @@ Requires:
classes: 'fa fa-picture-o'
}]
},
+ 'video': {
+ tag: 'button',
+ classes: 'btn btn-default',
+ attributes: [
+ {
+ name: 'title',
+ value: 'Video'
+ },
+ {
+ name: 'ng-click',
+ value: 'insertVideo()'
+ },
+ {
+ name: 'type',
+ value: 'button'
+ }
+ ],
+ data: [{
+ tag: 'i',
+ classes: 'fa fa-youtube-play'
+ }]
+ },
'font-color': {
tag: 'button',
classes: 'btn btn-default wysiwyg-colorpicker wysiwyg-fontcolor',
diff --git a/dist/angular-wysiwyg.min.js b/dist/angular-wysiwyg.min.js
index 1e94c44..10542b6 100644
--- a/dist/angular-wysiwyg.min.js
+++ b/dist/angular-wysiwyg.min.js
@@ -1 +1 @@
-!function(t,e){"use strict";var a=[["bold","italic","underline","strikethrough","subscript","superscript"],["format-block"],["font"],["font-size"],["font-color","hilite-color"],["remove-format"],["ordered-list","unordered-list","outdent","indent"],["left-justify","center-justify","right-justify"],["code","quote","paragraph"],["link","image"]];t.module("wysiwyg.module",["colorpicker.module"]).directive("wysiwyg",["$timeout","wysiwgGui","$compile",function(a,n,i){function l(l,s,o,r){function u(){c(),m(),f(),d()}function c(){n.setCustomElements(l.textareaCustomMenu);var t=s.children("div.wysiwyg-menu")[0];t.appendChild(n.createMenu(l.textareaMenu)),i(t)(l)}function m(){l.$watch("disabled",function(e){t.element("div.wysiwyg-menu").find("button").each(function(){t.element(this).attr("disabled",e)}),t.element("div.wysiwyg-menu").find("select").each(function(){t.element(this).attr("disabled",e)})})}function f(){"true"===o.enableBootstrapTitle&&o.enableBootstrapTitle!==e&&s.find("button[title]").tooltip({container:"body"})}function d(){t.element(".wysiwyg-menu").find("button").on("click",function(){var e=t.element(this);l.$emit("wysiwyg.click",e.attr("title")||e.attr("data-original-title"))}),b.on("input keyup paste mouseup",function(){var t=b.html();" "==t&&(t=""),r.$setViewValue(t)}),b.on("keydown",function(t){if(9==t.keyCode){{var e=(b.html(),window.getSelection());e.anchorOffset}t.preventDefault()}}),b.on("click keyup focus mouseup",function(){a(function(){l.isBold=l.cmdState("bold"),l.isUnderlined=l.cmdState("underline"),l.isStrikethrough=l.cmdState("strikethrough"),l.isItalic=l.cmdState("italic"),l.isSuperscript=g("SUP"),l.isSubscript=g("SUB"),l.isRightJustified=l.cmdState("justifyright"),l.isLeftJustified=l.cmdState("justifyleft"),l.isCenterJustified=l.cmdState("justifycenter"),l.isPre="pre"===l.cmdValue("formatblock"),l.isBlockquote="blockquote"===l.cmdValue("formatblock"),l.isOrderedList=l.cmdState("insertorderedlist"),l.isUnorderedList=l.cmdState("insertunorderedlist"),l.fonts.forEach(function(t){return l.cmdValue("fontname").indexOf(t)>-1?(l.font=t,!1):void 0}),l.cmdValue("formatblock").toLowerCase(),l.formatBlocks.forEach(function(t){return l.cmdValue("formatblock").toLowerCase()===t.value.toLowerCase()?(l.formatBlock=t,!1):void 0}),l.fontSizes.forEach(function(t){return l.cmdValue("fontsize")===t.value?(l.fontSize=t,!1):void 0}),l.hiliteColor=v(),s.find("button.wysiwyg-hiliteColor").css("background-color",l.hiliteColor),l.fontColor=l.cmdValue("forecolor"),s.find("button.wysiwyg-fontcolor").css("color",l.fontColor),l.isLink=g("A")},0)})}function g(t){var e=window.getSelection().getRangeAt(0);return e&&(e.startContainer.parentNode.tagName===t.toUpperCase()||e.endContainer.parentNode.tagName===t.toUpperCase())?!0:!1}function v(){var e=window.getSelection().getRangeAt(0);if(e){var a=t.element(e.startContainer.parentNode).attr("style");if(!t.isDefined(a))return!1;for(var n=a.split(";"),i=0;i',restrict:"E",scope:{value:"=ngModel",textareaHeight:"@textareaHeight",textareaName:"@textareaName",textareaClass:"@textareaClass",textareaRequired:"@textareaRequired",textareaId:"@textareaId",textareaMenu:"=textareaMenu",textareaCustomMenu:"=textareaCustomMenu",fn:"&",disabled:"=?disabled"},replace:!0,require:"ngModel",link:l,transclude:!0}}]).factory("wysiwgGui",["wysiwgGuiElements",function(e){function n(t){var e;if(t.tag)e=document.createElement(t.tag);else{if(!t.text)return console.log("cannot create this element."),e=document.createElement("span");e=document.createElement("span")}if(t.text&&document.all?e.innerText=t.text:e.textContent=t.text,t.classes&&(e.className=t.classes),t.html&&(e.innerHTML=t.html),t.attributes&&t.attributes.length)for(var a in t.attributes){var i=t.attributes[a];i.name&&i.value&&e.setAttribute(i.name,i.value)}if(t.data&&t.data.length)for(var l in t.data)e.appendChild(n(t.data[l]));return e}var i=e,l={},s=function(t){l=t},o=function(){return{tag:"div",classes:"btn-group btn-group-sm wysiwyg-btn-group-margin"}},r=function(t){return i[t]||{}},u=function(e){t.extend(i,l),e=t.isDefined(e)&&""!==e?e:a;for(var s,u=document.createElement("div"),c=0;c"===e&&(e=""),r.$setViewValue(e)}),p.on("keydown",function(e){if(9===e.keyCode){{var t=(p.html(),window.getSelection());t.anchorOffset}e.preventDefault()}}),p.on("click keyup focus mouseup",function(){a(function(){l.isBold=l.cmdState("bold"),l.isUnderlined=l.cmdState("underline"),l.isStrikethrough=l.cmdState("strikethrough"),l.isItalic=l.cmdState("italic"),l.isSuperscript=g("SUP"),l.isSubscript=g("SUB"),l.isRightJustified=l.cmdState("justifyright"),l.isLeftJustified=l.cmdState("justifyleft"),l.isCenterJustified=l.cmdState("justifycenter"),l.isPre="pre"===l.cmdValue("formatblock"),l.isBlockquote="blockquote"===l.cmdValue("formatblock"),l.isOrderedList=l.cmdState("insertorderedlist"),l.isUnorderedList=l.cmdState("insertunorderedlist"),l.fonts.forEach(function(e){return l.cmdValue("fontname").indexOf(e)>-1?(l.font=e,!1):void 0}),l.cmdValue("formatblock").toLowerCase(),l.formatBlocks.forEach(function(e){return l.cmdValue("formatblock").toLowerCase()===e.value.toLowerCase()?(l.formatBlock=e,!1):void 0}),l.fontSizes.forEach(function(e){return l.cmdValue("fontsize")===e.value?(l.fontSize=e,!1):void 0}),l.hiliteColor=v(),o.find("button.wysiwyg-hiliteColor").css("background-color",l.hiliteColor),l.fontColor=l.cmdValue("forecolor"),o.find("button.wysiwyg-fontcolor").css("color",l.fontColor),l.isLink=g("A")},0)})}function g(e){var t=window.getSelection().getRangeAt(0);return t&&(t.startContainer.parentNode.tagName===e.toUpperCase()||t.endContainer.parentNode.tagName===e.toUpperCase())?!0:!1}function v(){var t=window.getSelection().getRangeAt(0);if(t){var a=e.element(t.startContainer.parentNode).attr("style");if(!e.isDefined(a))return!1;for(var n=a.split(";"),i=0;i"},{name:"Heading 1",value:"
"},{name:"Heading 2",value:"
"},{name:"Heading 3",value:"
"},{name:"Heading 4",value:"
"},{name:"Heading 5",value:"
"},{name:"Heading 6",value:"
"}],l.formatBlock=l.formatBlocks[0],l.fontSize=l.fontSizes[1],e.isArray(l.cssClasses)&&(l.cssClasses.unshift("css"),l.cssClass=l.cssClasses[0]),l.fonts=["Georgia","Palatino Linotype","Times New Roman","Arial","Helvetica","Arial Black","Comic Sans MS","Impact","Lucida Sans Unicode","Tahoma","Trebuchet MS","Verdana","Courier New","Lucida Console","Helvetica Neue"].sort(),l.font=l.fonts[6],u(),r.$render=function(){p.html(r.$viewValue)},l.format=function(e,t){document.execCommand(e,!0,t)},l.cmdState=function(e){return document.queryCommandState(e)},l.cmdValue=function(e){return document.queryCommandValue(e)},l.createLink=function(){var e=prompt("Enter the link URL");e&&e!==t&&l.format("createlink",e)},l.insertImage=function(){var e=prompt("Enter the image URL");e&&e!==t&&l.format("insertimage",e)},l.insertVideo=function(){var e=document.getSelection();if(e.rangeCount<=0)return void console.log("No range selected");var a=prompt("Enter the video URL");if(a&&a!==t){var n;null!==(n=/youtube\.com\/watch\?v=(.*)/.exec(a))?a="https://www.youtube.com/embed/"+n[1]:null!==(n=/[^player]\/vimeo\.com\/(.*)/.exec(a))&&(a="https://player.vimeo.com/video/"+n[1]);var i="",o=e.getRangeAt(0);if(null===b(o.startContainer,l.textareaId))return;var s=o.createContextualFragment(i);o.insertNode(s)}},l.setFont=function(){l.format("fontname",l.font)},l.setFontSize=function(){l.format("fontsize",l.fontSize.value)},l.setFormatBlock=function(){l.format("formatBlock",l.formatBlock.value)},l.setFontColor=function(){l.format("forecolor",l.fontColor)},l.setHiliteColor=function(){l.format("hiliteColor",l.hiliteColor)},l.format("enableobjectresizing",!0),l.format("styleWithCSS",!0)}return{template:'
',restrict:"E",scope:{value:"=ngModel",textareaHeight:"@textareaHeight",textareaName:"@textareaName",textareaClass:"@textareaClass",textareaRequired:"@textareaRequired",textareaId:"@textareaId",textareaMenu:"=textareaMenu",textareaCustomMenu:"=textareaCustomMenu",fn:"&",disabled:"=?disabled"},replace:!0,require:"ngModel",link:l,transclude:!0}}]).factory("wysiwgGui",["wysiwgGuiElements",function(t){function n(e){var t;if(e.tag)t=document.createElement(e.tag);else{if(!e.text)return console.log("cannot create this element."),t=document.createElement("span");t=document.createElement("span")}if(e.text&&document.all?t.innerText=e.text:e.text&&(t.textContent=e.text),e.classes&&(t.className=e.classes),e.html&&(t.innerHTML=e.html),e.attributes&&e.attributes.length)for(var a in e.attributes){var i=e.attributes[a];i.name&&i.value&&t.setAttribute(i.name,i.value)}if(e.data&&e.data.length)for(var l in e.data)t.appendChild(n(e.data[l]));return t}var i=t,l={},o=function(e){l=e},s=function(){return{tag:"div",classes:"btn-group btn-group-sm wysiwyg-btn-group-margin"}},r=function(e){return i[e]||{}},u=function(t){e.extend(i,l),t=e.isDefined(t)&&""!==t?t:a;for(var o,u=document.createElement("div"),c=0;c'
}, {
name: 'Heading 1',
- value: 'h1'
+ value: '