From 5cc68e5ad515625d424dfcc5340aaeb7c84cea4c Mon Sep 17 00:00:00 2001 From: Muhammad Raza Dar Date: Mon, 29 Feb 2016 18:06:09 +0500 Subject: [PATCH] Adding Raza changes and Hussnain style changes and updating source. --- dist/wysiwyg.min.js | 916 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 915 insertions(+), 1 deletion(-) diff --git a/dist/wysiwyg.min.js b/dist/wysiwyg.min.js index b5d1aa8..a9a780c 100644 --- a/dist/wysiwyg.min.js +++ b/dist/wysiwyg.min.js @@ -1 +1,915 @@ -!function(e,t){"object"==typeof exports?module.exports=t(require("angular")):"function"==typeof define&&define.amd?define(["angular"],t):t(e.angular)}(this,function(e){"use strict";e.module("ngWYSIWYG",["ngSanitize"]),e.module("ngWYSIWYG").config(["$provide",function(e){e.decorator("$sanitize",["$delegate","$log",function(e,t){return function(t,n){var o=e(t,n);return o}}])}]),e.module("ngWYSIWYG").constant("NGP_EVENTS",{ELEMENT_CLICKED:"ngp-element-clicked",CLICK_AWAY:"ngp-click-away"}),e.module("ngWYSIWYG").directive("ngpColorsGrid",["NGP_EVENTS",function(e){var t=function(t,n){t.$on(e.CLICK_AWAY,function(){t.$apply(function(){t.show=!1})}),n.parent().bind("click",function(e){e.stopPropagation()}),t.colors=["#000000","#993300","#333300","#003300","#003366","#000080","#333399","#333333","#800000","#FF6600","#808000","#008000","#008080","#0000FF","#666699","#808080","#FF0000","#FF9900","#99CC00","#339966","#33CCCC","#3366FF","#800080","#999999","#FF00FF","#FFCC00","#FFFF00","#00FF00","#00FFFF","#00CCFF","#993366","#C0C0C0","#FF99CC","#FFCC99","#FFFF99","#CCFFCC","#CCFFFF","#99CCFF","#CC99FF","#FFFFFF"],t.pick=function(e){t.onPick({color:e})},n.ready(function(){function e(t){1==t.nodeType&&(t.setAttribute("unselectable","on"),t.unselectable="on");for(var n=t.firstChild;n;)e(n),n=n.nextSibling}for(var t=0;t
  • '}}]),e.module("ngWYSIWYG").directive("ngpSymbolsGrid",["NGP_EVENTS",function(e){var t=function(t,n){t.$on(e.CLICK_AWAY,function(){t.$apply(function(){t.show=!1})}),n.parent().bind("click",function(e){e.stopPropagation()}),t.symbols=["¡","¿","–","—","»","«","©","÷","µ","¶","±","¢","€","£","®","§","™","¥","°","∀","∂","∃","∅","∇","∈","∉","∋","∏","∑","↑","→","↓","♠","♣","♥","♦","á","à","â","å","ã","ä","æ","ç","é","è","ê","ë","í","ì","î","ï","ñ","ó","ò","ô","ø","õ","ö","ß","ú","ù","û","ü","ÿ"],t.pick=function(e){t.onPick({symbol:e})},n.ready(function(){function e(t){1==t.nodeType&&(t.setAttribute("unselectable","on"),t.unselectable="on");for(var n=t.firstChild;n;)e(n),n=n.nextSibling}for(var t=0;t
  • '}}]),e.module("ngWYSIWYG").service("ngpImageResizer",["NGP_EVENTS",function(e){function t(e){e.preventDefault()}function n(e){e.preventDefault(),e.stopPropagation(),v.style.height="",v.style.width="",l()}function o(e){e.preventDefault(),e.stopPropagation(),v.style.width="100%",v.style.height="",l()}function i(e){return e.target!=y?(c.removeEventListener("mousemove",r),void(h=!1)):(e.stopPropagation(),e.preventDefault(),c.addEventListener("mousemove",r),void(h=!0))}function r(e){e.stopPropagation(),e.preventDefault();var t=e.pageY,n=t-(v.getBoundingClientRect().top+d.pageYOffset);v.style.height=n+"px",v.style.width="",g&&e.clientY>g&&d.innerHeight-e.clientY<=45&&d.scrollTo(0,d.innerHeight),g=e.clientY,l()}function a(e,t){return t==p||h?void c.removeEventListener("mousemove",r):"IMG"!==t.tagName?s():(p.parentNode||u.appendChild(p),v=t,void l())}function l(){var e=d.getComputedStyle(v);p.style.height=e.getPropertyValue("height"),p.style.width=e.getPropertyValue("width"),p.style.top=v.getBoundingClientRect().top+d.pageYOffset+"px",p.style.left=v.getBoundingClientRect().left+d.pageXOffset+"px",p.style.display="block"}function s(e){p.parentNode&&(e&&"IMG"===e.target.tagName||(p.style.display="none",g=null))}var c,d,u,p,g,m,y,f,h,v,b=this;b.setup=function(r,l){d=l.defaultView,c=l,u=c.querySelector("body"),m=r,p=c.createElement("div"),p.className="ngp-image-resizer",p.style.position="absolute",p.style.border="1px dashed black",p.style.display="none",p.setAttribute("contenteditable",!1),y=c.createElement("div"),y.style.position="absolute",y.style.height="10px",y.style.width="10px",y.style.bottom="-5px",y.style.right="-5px",y.style.border="1px solid black",y.style.backgroundColor="#fff",y.style.cursor="se-resize",y.setAttribute("contenteditable",!1),p.appendChild(y),f=c.createElement("div"),f.style.position="absolute",f.style.height="30px",f.style.width="150px",f.style.bottom="-30px",f.style.left="0",p.appendChild(f);var g=c.createElement("button");g.addEventListener("click",n),g.innerHTML="Auto",f.appendChild(g);var h=c.createElement("button");h.addEventListener("click",o),h.innerHTML="100%",f.appendChild(h),c.addEventListener("mousedown",i),c.addEventListener("mouseup",i),d.parent.document.addEventListener("mouseup",i),u.addEventListener("mscontrolselect",t),m.$on(e.ELEMENT_CLICKED,a),m.$on(e.CLICK_AWAY,s)}}]);var t='
    {toolbar}
    ';return e.module("ngWYSIWYG").directive("wysiwygEdit",["ngpUtils","NGP_EVENTS","$rootScope","$compile","$timeout","$q",function(n,o,i,r,a,l){var s=function(a,s,c,d){function u(){null==m&&(m=document.querySelector("wysiwyg-edit").querySelector("iframe"),y=m.contentDocument,f=y.defaultView)}function p(e){a.$broadcast("insertElement",e)}function g(e){var t="<"+e.type;if(t+=' class="'+e["class"],h&&(t+=" tinyeditor-control-fa"),t+='" ',"div"==e.type){if(e.title&&(t+='title="'+e.title+'" '),e.backgroundPos&&!h&&(t+='style="background-position: '+e.backgroundPos+'; position: relative;" '),e.pressed&&(t+="ng-class=\"{'pressed': cursorStyle."+e.pressed+'}" '),e.command){var n="'"+e.command+"'";e.commandParameter&&(n+=", '"+e.commandParameter+"'"),t+='ng-click="execCommand('+n+')" '}else e.specialCommand&&(t+='ng-click="'+e.specialCommand+'" ');t+=">",e.faIcon&&h&&"-"!=e.faIcon&&(t+=''),e.faIcon&&h&&"-"==e.faIcon&&(t+='
    '),e.inner&&(t+=e.inner)}else"select"==e.type&&(t+='ng-model="'+e.model+'" ',t+='ng-options="'+e.options+'" ',t+='ng-change="'+e.change+'" ',t+='");return t+=""}a.editMode=!1,a.cursorStyle={},document.addEventListener("click",function(){i.$broadcast(o.CLICK_AWAY)});var m=null,y=null,f=null;a.panelButtons={"-":{type:"div","class":"tinyeditor-divider"},bold:{type:"div",title:"Bold","class":"tinyeditor-control",faIcon:"bold",backgroundPos:"34px -120px",pressed:"bold",command:"bold"},italic:{type:"div",title:"Italic","class":"tinyeditor-control",faIcon:"italic",backgroundPos:"34px -150px",pressed:"italic",command:"italic"},underline:{type:"div",title:"Underline","class":"tinyeditor-control",faIcon:"underline",backgroundPos:"34px -180px",pressed:"underline",command:"underline"},strikethrough:{type:"div",title:"Strikethrough","class":"tinyeditor-control",faIcon:"strikethrough",backgroundPos:"34px -210px",pressed:"strikethrough",command:"strikethrough"},subscript:{type:"div",title:"Subscript","class":"tinyeditor-control",faIcon:"subscript",backgroundPos:"34px -240px",pressed:"sub",command:"subscript"},superscript:{type:"div",title:"Superscript","class":"tinyeditor-control",faIcon:"superscript",backgroundPos:"34px -270px",pressed:"super",command:"superscript"},leftAlign:{type:"div",title:"Left Align","class":"tinyeditor-control",faIcon:"align-left",backgroundPos:"34px -420px",pressed:"alignmet == 'left'",command:"justifyleft"},centerAlign:{type:"div",title:"Center Align","class":"tinyeditor-control",faIcon:"align-center",backgroundPos:"34px -450px",pressed:"alignment == 'center'",command:"justifycenter"},rightAlign:{type:"div",title:"Right Align","class":"tinyeditor-control",faIcon:"align-right",backgroundPos:"34px -480px",pressed:"alignment == 'right'",command:"justifyright"},blockJustify:{type:"div",title:"Block Justify","class":"tinyeditor-control",faIcon:"align-justify",backgroundPos:"34px -510px",pressed:"alignment == 'justify'",command:"justifyfull"},orderedList:{type:"div",title:"Insert Ordered List","class":"tinyeditor-control",faIcon:"list-ol",backgroundPos:"34px -300px",command:"insertorderedlist"},unorderedList:{type:"div",title:"Insert Unordered List","class":"tinyeditor-control",faIcon:"list-ul",backgroundPos:"34px -330px",command:"insertunorderedlist"},outdent:{type:"div",title:"Outdent","class":"tinyeditor-control",faIcon:"outdent",backgroundPos:"34px -360px",command:"outdent"},indent:{type:"div",title:"Indent","class":"tinyeditor-control",faIcon:"indent",backgroundPos:"34px -390px",command:"indent"},removeFormatting:{type:"div",title:"Remove Formatting","class":"tinyeditor-control",faIcon:"eraser",backgroundPos:"34px -720px",command:"removeformat"},undo:{type:"div",title:"Undo","class":"tinyeditor-control",faIcon:"undo",backgroundPos:"34px -540px",command:"undo"},redo:{type:"div",title:"Redo","class":"tinyeditor-control",faIcon:"repeat",backgroundPos:"34px -570px",command:"redo"},fontColor:{type:"div",title:"Font Color","class":"tinyeditor-control",faIcon:"font",backgroundPos:"34px -779px",specialCommand:"showFontColors = !showFontColors",inner:''},backgroundColor:{type:"div",title:"Background Color","class":"tinyeditor-control",faIcon:"paint-brush",backgroundPos:"34px -808px",specialCommand:"showBgColors = !showBgColors",inner:''},image:{type:"div",title:"Insert Image","class":"tinyeditor-control",faIcon:"picture-o",backgroundPos:"34px -600px",specialCommand:"insertImage()"},hr:{type:"div",title:"Insert Horizontal Rule","class":"tinyeditor-control",faIcon:"-",backgroundPos:"34px -630px",command:"inserthorizontalrule"},symbols:{type:"div",title:"Insert Special Symbol","class":"tinyeditor-control",faIcon:"cny",backgroundPos:"34px -838px",specialCommand:"showSpecChars = !showSpecChars",inner:''},link:{type:"div",title:"Insert Hyperlink","class":"tinyeditor-control",faIcon:"link",backgroundPos:"34px -660px",specialCommand:"insertLink()"},unlink:{type:"div",title:"Remove Hyperlink","class":"tinyeditor-control",faIcon:"chain-broken",backgroundPos:"34px -690px",command:"unlink"},print:{type:"div",title:"Print","class":"tinyeditor-control",faIcon:"print",backgroundPos:"34px -750px",command:"print"},font:{type:"select",title:"Font","class":"tinyeditor-font",model:"font",options:"a as a for a in fonts",change:"fontChange()"},size:{type:"select",title:"Size","class":"tinyeditor-size",model:"fontsize",options:"a.key as a.name for a in fontsizes",change:"sizeChange()"},format:{type:"select",title:"Style","class":"tinyeditor-size",model:"textstyle",options:"s.key as s.name for s in styles",change:"styleChange()"}};var h=a.config&&a.config.fontAwesome;a.toolbar=a.config&&a.config.toolbar?a.config.toolbar:[{name:"basicStyling",items:["bold","italic","underline","strikethrough","subscript","superscript","leftAlign","centerAlign","rightAlign","blockJustify","-"]},{name:"paragraph",items:["orderedList","unorderedList","outdent","indent","-"]},{name:"doers",items:["removeFormatting","undo","redo","-"]},{name:"colors",items:["fontColor","backgroundColor","-"]},{name:"links",items:["image","hr","symbols","link","unlink","-"]},{name:"tools",items:["print","-"]},{name:"styling",items:["font","size","format"]}];var v=[];e.forEach(a.toolbar,function(t,n){var o=[];e.forEach(t.items,function(e,t){var n=a.panelButtons[e];n||(n=a.config.buttons[e]),this.push(g(n))},o),this.push('
    '+o.join("")+"
    ")},v);var b=t.replace("{toolbar}",v.join(""));b=b.replace("{contentStyle}",c.contentStyle||""),s.html(b),r(s.contents())(a),a.execCommand=function(e,t){switch(e){case"bold":a.cursorStyle.bold=!a.cursorStyle.bold;break;case"italic":a.cursorStyle.italic=!a.cursorStyle.italic;break;case"underline":a.cursorStyle.underline=!a.cursorStyle.underline;break;case"strikethrough":a.cursorStyle.strikethrough=!a.cursorStyle.strikethrough;break;case"subscript":a.cursorStyle.sub=!a.cursorStyle.sub;break;case"superscript":a.cursorStyle["super"]=!a.cursorStyle["super"];break;case"justifyleft":a.cursorStyle.alignment="left";break;case"justifycenter":a.cursorStyle.alignment="center";break;case"justifyright":a.cursorStyle.alignment="right";break;case"justifyfull":a.cursorStyle.alignment="justify"}a.$broadcast("execCommand",{command:e,arg:t})},a.fonts=["Verdana","Arial","Arial Black","Arial Narrow","Courier New","Century Gothic","Comic Sans MS","Georgia","Impact","Tahoma","Times","Times New Roman","Webdings","Trebuchet MS"],a.fontChange=function(){a.execCommand("fontname",a.font)},a.fontsizes=[{key:1,name:"x-small"},{key:2,name:"small"},{key:3,name:"normal"},{key:4,name:"large"},{key:5,name:"x-large"},{key:6,name:"xx-large"},{key:7,name:"xxx-large"}],a.mapFontSize={10:1,13:2,16:3,18:4,24:5,32:6,48:7},a.sizeChange=function(){a.execCommand("fontsize",a.fontsize)},a.styles=[{name:"Paragraph",key:"

    "},{name:"Header 1",key:"

    "},{name:"Header 2",key:"

    "},{name:"Header 3",key:"

    "},{name:"Header 4",key:"

    "},{name:"Header 5",key:"

    "},{name:"Header 6",key:"
    "}],a.styleChange=function(){a.execCommand("formatblock",a.textstyle)},a.showFontColors=!1,a.setFontColor=function(e){a.execCommand("foreColor",e)},a.showBgColors=!1,a.setBgColor=function(e){a.execCommand("hiliteColor",e)},a.showSpecChars=!1,a.insertSpecChar=function(e){p(e)},a.insertLink=function(){if(u(),null!=f.getSelection().focusNode){var t=n.getSelectionBoundaryElement(f,!0),o="http://";if(t&&"A"==t.nodeName){o=t.href;var i=y.createRange();i.setStart(t.firstChild,0),i.setEnd(t.firstChild,t.firstChild.length);var r=f.getSelection();r.removeAllRanges(),r.addRange(i)}var s;s=a.api&&a.api.insertLink&&e.isFunction(a.api.insertLink)?a.api.insertLink.apply(a.api.scope||null,[o]):prompt("Please enter the URL","http://"),l.when(s).then(function(e){a.execCommand("createlink",e)})}},a.insertImage=function(){var t;a.api&&a.api.insertImage&&e.isFunction(a.api.insertImage)?t=a.api.insertImage.apply(a.api.scope||null):(t=prompt("Please enter the picture URL","http://"),t=''),l.when(t).then(function(e){p(e)})},s.ready(function(){function e(t){1==t.nodeType&&(t.setAttribute("unselectable","on"),t.unselectable="on");for(var n=t.firstChild;n;)e(n),n=n.nextSibling}for(var t=0;t'),d.close(),d.designMode="On",t.setup(i,d);var u=e.element(l[0].contentDocument.body),p=e.element(l[0].contentDocument.head);u.attr("contenteditable","true"),d.addEventListener("click",function(e){"HTML"===e.target.tagName&&e.target.querySelector("body").focus(),i.$emit(o.ELEMENT_CLICKED,e.target)}),s.contentStyle&&p.append(''),c.$render=function(){u[0].innerHTML=c.$viewValue?i.config&&i.config.sanitize?a(c.$viewValue):c.$viewValue:""},i.sync=function(){i.$evalAsync(function(e){c.$setViewValue(u.html())})};var g=null;u.bind("click keyup change paste",function(){g&&r.cancel(g),g=r(function(){var e=u[0].ownerDocument,t=e.querySelector(".ngp-image-resizer"),o=u[0].innerHTML;t&&(o=o.replace(t.outerHTML,"")),c.$setViewValue(o);var r=n.getSelectionBoundaryElement(l[0].contentWindow,!0);if(r){var a=l[0].contentWindow.getComputedStyle(r),s={bold:"bold"==a.getPropertyValue("font-weight")||parseInt(a.getPropertyValue("font-weight"))>=700,italic:"italic"==a.getPropertyValue("font-style"),underline:"underline"==a.getPropertyValue("text-decoration"),strikethrough:"line-through"==a.getPropertyValue("text-decoration"),font:a.getPropertyValue("font-family"),size:parseInt(a.getPropertyValue("font-size")),color:a.getPropertyValue("color"),sub:"sub"==a.getPropertyValue("vertical-align"),"super":"super"==a.getPropertyValue("vertical-align"),background:a.getPropertyValue("background-color"),alignment:a.getPropertyValue("text-align")};i.$emit("cursor-position",s)}},100,!0)}),i.range=null,i.getSelection=function(){if(d.getSelection){var e=d.getSelection();e.getRangeAt&&e.rangeCount&&(i.range=e.getRangeAt(0))}},i.restoreSelection=function(){if(i.range&&d.getSelection){var e=d.getSelection();e.removeAllRanges(),e.addRange(i.range)}},i.$on("execCommand",function(e,t){console.log("execCommand: "),console.log(t),l[0].contentDocument.body.focus();var n=d.selection;if(n){var o=n.createRange();d.execCommand(t.command,0,t.arg),o.collapse(!1),o.select()}else d.execCommand(t.command,0,t.arg);d.body.focus(),i.sync()}),i.$on("insertElement",function(e,t){var n,o;if(d.defaultView.getSelection){if(n=d.defaultView.getSelection(),n.getRangeAt&&n.rangeCount){o=n.getRangeAt(0),o.deleteContents();var r=d.createElement("div");r.innerHTML=t;for(var a,l,s=d.createDocumentFragment();a=r.firstChild;)l=s.appendChild(a);s.firstChild;o.insertNode(s),l&&(o=o.cloneRange(),o.setStartAfter(l),o.collapse(!0),n.removeAllRanges(),n.addRange(o))}}else d.selection&&"Control"!=d.selection.type&&d.selection.createRange().pasteHTML(t);i.sync()}),i.$on("$destroy",function(){});try{d.execCommand("styleWithCSS",0,0),d.execCommand("enableObjectResizing",!1,"false"),d.execCommand("contentReadOnly",0,"false")}catch(m){try{d.execCommand("useCSS",0,1)}catch(m){}}};return{link:l,require:"ngModel",scope:{config:"=ngpContentFrame"},replace:!0,restrict:"AE"}}]),e.module("ngWYSIWYG").directive("ngpResizable",["$document",function(e){return function(t,n){var o=e[0],i=n[0],r=o.createElement("span");r.className="resizer",i.appendChild(r),r.addEventListener("mousedown",function(){function e(e){e.preventDefault();var t=e.pageY;e.view!=o.defaultView&&(t=e.pageY+e.view.frameElement.getBoundingClientRect().top+o.defaultView.pageYOffset);var n=t-(i.getBoundingClientRect().top+o.defaultView.pageYOffset),r=i.style.height.replace("px","");r&&n>r&&window.innerHeight-e.clientY<=45&&o.defaultView.scrollBy(0,n-r),i.style.height=n+"px"}function t(){o.removeEventListener("mousemove",e),o.removeEventListener("mouseup",t);for(var n=o.querySelectorAll("iframe"),i=0;i0&&(n=o.getRangeAt(0),i=n[t?"startContainer":"endContainer"],3===i.nodeType&&(i=i.parentNode))):e.getSelection&&(o=e.getSelection(),o.rangeCount>0&&(n=o.getRangeAt(0),i=n[t?"startContainer":"endContainer"],3===i.nodeType&&(i=i.parentNode))),i)}}]),"ngWYSIWYG"}); \ No newline at end of file +(function(root, factory) { +if (typeof exports === "object") { +module.exports = factory(require('angular')); +} else if (typeof define === "function" && define.amd) { +define(['angular'], factory); +} else{ +factory(root.angular); +} +}(this, function(angular) { +'use strict'; + +angular.module('ngWYSIWYG', ['ngSanitize']); + +//debug sanitize +angular.module('ngWYSIWYG').config(['$provide', + //http://odetocode.com/blogs/scott/archive/2014/09/10/a-journey-with-trusted-html-in-angularjs.aspx + function($provide) { + $provide.decorator("$sanitize",['$delegate', '$log', function($delegate, $log) { + return function(text, target) { + var result = $delegate(text, target); + //$log.info("$sanitize input: " + text); + //$log.info("$sanitize output: " + result); + return result; + }; + }]); + } +]); + +angular.module('ngWYSIWYG').constant('NGP_EVENTS', { + ELEMENT_CLICKED: 'ngp-element-clicked', + CLICK_AWAY: 'ngp-click-away' +}); + +angular.module('ngWYSIWYG').directive('ngpColorsGrid', ['NGP_EVENTS', function(NGP_EVENTS) { + var linker = function (scope, element) { + + //click away + scope.$on(NGP_EVENTS.CLICK_AWAY, function() { + scope.$apply(function() { + scope.show = false; + }); + }); + + element.parent().bind('click', function(e) { + e.stopPropagation(); + }); + + scope.colors = [ + '#000000', '#993300', '#333300', '#003300', '#003366', '#000080', '#333399', '#333333', + '#800000', '#FF6600', '#808000', '#008000', '#008080', '#0000FF', '#666699', '#808080', + '#FF0000', '#FF9900', '#99CC00', '#339966', '#33CCCC', '#3366FF', '#800080', '#999999', + '#FF00FF', '#FFCC00', '#FFFF00', '#00FF00', '#00FFFF', '#00CCFF', '#993366', '#C0C0C0', + '#FF99CC', '#FFCC99', '#FFFF99', '#CCFFCC', '#CCFFFF', '#99CCFF', '#CC99FF', '#FFFFFF' + ]; + + scope.pick = function( color ) { + scope.onPick({color: color}); + }; + + element.ready(function() { + //real deal for IE + function makeUnselectable(node) { + if (node.nodeType == 1) { + node.setAttribute("unselectable", "on"); + node.unselectable = 'on'; + } + var child = node.firstChild; + while (child) { + makeUnselectable(child); + child = child.nextSibling; + } + } + //IE fix + for(var i = 0; i < document.getElementsByClassName('ngp-colors-grid').length; i += 1) { + makeUnselectable(document.getElementsByClassName("ngp-colors-grid")[i]); + } + }); + }; + return { + link: linker, + scope: { + show: '=', + onPick: '&' + }, + restrict: 'AE', + template: '
    ' + }; +}]); +angular.module('ngWYSIWYG').directive('ngpSymbolsGrid', ['NGP_EVENTS', function(NGP_EVENTS) { + var linker = function (scope, element) { + + scope.$on(NGP_EVENTS.CLICK_AWAY, function() { + scope.$apply(function() { + scope.show = false; + }); + }); + + element.parent().bind('click', function(e) { + e.stopPropagation(); + }); + + scope.symbols = [ + '¡', '¿', '–', '—', '»', '«', '©', + '÷', 'µ', '¶', '±', '¢', '€', '£', '®', + '§', '™', '¥', '°', '∀', '∂', '∃', '∅', + '∇', '∈', '∉', '∋', '∏', '∑', '↑', '→', '↓', + '♠', '♣', '♥', '♦', 'á', 'à', 'â', 'å', + 'ã', 'ä', 'æ', 'ç', 'é', 'è', 'ê', 'ë', + 'í', 'ì', 'î', 'ï', 'ñ', 'ó', 'ò', + 'ô', 'ø', 'õ', 'ö', 'ß', 'ú', 'ù', + 'û', 'ü', 'ÿ' + ]; + + scope.pick = function( symbol ) { + scope.onPick({symbol: symbol}); + }; + + element.ready(function() { + //real deal for IE + function makeUnselectable(node) { + if (node.nodeType == 1) { + node.setAttribute("unselectable", "on"); + node.unselectable = 'on'; + } + var child = node.firstChild; + while (child) { + makeUnselectable(child); + child = child.nextSibling; + } + } + //IE fix + for(var i = 0; i < document.getElementsByClassName('ngp-symbols-grid').length; i += 1) { + makeUnselectable(document.getElementsByClassName("ngp-symbols-grid")[i]); + } + }); + }; + return { + link: linker, + scope: { + show: '=', + onPick: '&' + }, + restrict: 'AE', + template: '
    ' + } +}]); +angular.module('ngWYSIWYG').service('ngpImageResizer', ['NGP_EVENTS', function(NGP_EVENTS) { + var service = this; + var iframeDoc, iframeWindow, iframeBody, resizerContainer, lastVerticalCursorPosition, + iframeScope, keepRatioButton, resizerOptionsContainer, resizing, elementBeingResized; + + service.setup = function(scope, document) { + iframeWindow = document.defaultView; + iframeDoc = document; + iframeBody = iframeDoc.querySelector('body'); + iframeScope = scope; + + // creating resizer container + resizerContainer = iframeDoc.createElement('div'); + resizerContainer.className = 'ngp-image-resizer'; + resizerContainer.style.position = 'absolute'; + resizerContainer.style.border = '1px dashed black'; + resizerContainer.style.display = 'none'; + resizerContainer.setAttribute('contenteditable', false); + + // creating bottom-right resizer button + keepRatioButton = iframeDoc.createElement('div'); + keepRatioButton.style.position = 'absolute'; + keepRatioButton.style.height = '10px'; + keepRatioButton.style.width = '10px'; + keepRatioButton.style.bottom = '-5px'; + keepRatioButton.style.right = '-5px'; + keepRatioButton.style.border = '1px solid black'; + keepRatioButton.style.backgroundColor = '#fff'; + keepRatioButton.style.cursor = 'se-resize'; + keepRatioButton.setAttribute('contenteditable', false); + resizerContainer.appendChild(keepRatioButton); + + // resizer options container + resizerOptionsContainer = iframeDoc.createElement('div'); + resizerOptionsContainer.style.position = 'absolute'; + resizerOptionsContainer.style.height = '30px'; + resizerOptionsContainer.style.width = '150px'; + resizerOptionsContainer.style.bottom = '-30px'; + resizerOptionsContainer.style.left = '0'; + resizerContainer.appendChild(resizerOptionsContainer); + + // resizer options + var resizerReset = iframeDoc.createElement('button'); + resizerReset.addEventListener('click', resetImageSize); + resizerReset.innerHTML = 'Auto'; + resizerOptionsContainer.appendChild(resizerReset); + + var resizer100 = iframeDoc.createElement('button'); + resizer100.addEventListener('click', size100); + resizer100.innerHTML = '100%'; + resizerOptionsContainer.appendChild(resizer100); + + // resizer listener + iframeDoc.addEventListener('mousedown', startResizing); + iframeDoc.addEventListener('mouseup', startResizing); + iframeWindow.parent.document.addEventListener('mouseup', startResizing); + + iframeBody.addEventListener('mscontrolselect', disableIESelect); + + // listening to events + iframeScope.$on(NGP_EVENTS.ELEMENT_CLICKED, createResizer); + iframeScope.$on(NGP_EVENTS.CLICK_AWAY, removeResizer); + }; + + function disableIESelect(event) { + event.preventDefault(); + } + + function resetImageSize(event) { + event.preventDefault(); + event.stopPropagation(); + elementBeingResized.style.height = ''; + elementBeingResized.style.width = ''; + updateResizer(); + } + + function size100(event) { + event.preventDefault(); + event.stopPropagation(); + elementBeingResized.style.width = '100%'; + elementBeingResized.style.height = ''; + updateResizer(); + } + + function startResizing(event) { + if (event.target != keepRatioButton) { + iframeDoc.removeEventListener('mousemove', updateImageSize); + resizing = false; + return; + } + event.stopPropagation(); + event.preventDefault(); + iframeDoc.addEventListener('mousemove', updateImageSize); + resizing = true; + } + + function updateImageSize(event) { + event.stopPropagation(); + event.preventDefault(); + + var cursorVerticalPosition = event.pageY; + var newHeight = cursorVerticalPosition - + (elementBeingResized.getBoundingClientRect().top + iframeWindow.pageYOffset); + elementBeingResized.style.height = newHeight + 'px'; + elementBeingResized.style.width = ''; + + if (lastVerticalCursorPosition && event.clientY > lastVerticalCursorPosition + && iframeWindow.innerHeight - event.clientY <= 45) { + iframeWindow.scrollTo(0, iframeWindow.innerHeight); + } + lastVerticalCursorPosition = event.clientY; + updateResizer(); + } + + function createResizer(event, element) { + if (element == resizerContainer || resizing) { + iframeDoc.removeEventListener('mousemove', updateImageSize); + return; + } + if (element.tagName !== 'IMG') { + return removeResizer(); + } + if (!resizerContainer.parentNode) { + iframeBody.appendChild(resizerContainer); + } + elementBeingResized = element; + updateResizer(); + } + + function updateResizer() { + var elementStyle = iframeWindow.getComputedStyle(elementBeingResized); + resizerContainer.style.height = elementStyle.getPropertyValue('height'); + resizerContainer.style.width = elementStyle.getPropertyValue('width'); + resizerContainer.style.top = (elementBeingResized.getBoundingClientRect().top + iframeWindow.pageYOffset) + 'px'; + resizerContainer.style.left = (elementBeingResized.getBoundingClientRect().left + iframeWindow.pageXOffset) + 'px'; + resizerContainer.style.display = 'block'; + } + + function removeResizer(event) { + if (!resizerContainer.parentNode) { + return; + } + if (event && event.target.tagName === 'IMG') { + return; + } + resizerContainer.style.display = 'none'; + lastVerticalCursorPosition = null; + } +}]); +var editorTemplate = "
    " + + "
    " + + "{toolbar}" + // <-- we gonna replace it with the configured toolbar + "
    " + + "
    " + + "
    " + + "" + + "" + + "
    " + + "
    " + + "
    wysiwygsource
    " + + "
    " + + "
    "; + +angular.module('ngWYSIWYG').directive('wysiwygEdit', ['ngpUtils', 'NGP_EVENTS', '$rootScope', '$compile', '$timeout', '$q', + function(ngpUtils, NGP_EVENTS, $rootScope, $compile, $timeout, $q) { + var linker = function( scope, $element, attrs, ctrl ) { + scope.editMode = false; + scope.cursorStyle = {}; //current cursor/caret position style + + document.addEventListener('click', function() { + $rootScope.$broadcast(NGP_EVENTS.CLICK_AWAY); + }); + + var iframe = null; + var iframeDocument = null; + var iframeWindow = null; + + function loadVars() { + if (iframe != null) return; + iframe = document.querySelector('wysiwyg-edit').querySelector('iframe'); + iframeDocument = iframe.contentDocument; + iframeWindow = iframeDocument.defaultView; + } + + function insertElement(html) { + scope.$broadcast('insertElement', html); + } + + scope.panelButtons = { + '-': { type: 'div', class: 'tinyeditor-divider' }, + bold: { type: 'div', title: 'Bold', class: 'tinyeditor-control', faIcon: 'bold', backgroundPos: '34px -120px', pressed: 'bold', command: 'bold' }, + italic:{type: 'div', title: 'Italic', class: 'tinyeditor-control', faIcon: 'italic', backgroundPos: '34px -150px', pressed: 'italic', command: 'italic' }, + underline:{ type: 'div', title: 'Underline', class: 'tinyeditor-control', faIcon: 'underline', backgroundPos: '34px -180px', pressed: 'underline', command: 'underline' }, + strikethrough:{ type: 'div', title: 'Strikethrough', class: 'tinyeditor-control', faIcon: 'strikethrough', backgroundPos: '34px -210px', pressed: 'strikethrough', command: 'strikethrough' }, + subscript:{ type: 'div', title: 'Subscript', class: 'tinyeditor-control', faIcon: 'subscript', backgroundPos: '34px -240px', pressed: 'sub', command: 'subscript' }, + superscript:{ type: 'div', title: 'Superscript', class: 'tinyeditor-control', faIcon: 'superscript', backgroundPos: '34px -270px', pressed: 'super', command: 'superscript' }, + leftAlign:{ type: 'div', title: 'Left Align', class: 'tinyeditor-control', faIcon: 'align-left', backgroundPos: '34px -420px', pressed: 'alignmet == \'left\'', command: 'justifyleft' }, + centerAlign:{ type: 'div', title: 'Center Align', class: 'tinyeditor-control', faIcon: 'align-center', backgroundPos: '34px -450px', pressed: 'alignment == \'center\'', command: 'justifycenter' }, + rightAlign:{ type: 'div', title: 'Right Align', class: 'tinyeditor-control', faIcon: 'align-right', backgroundPos: '34px -480px', pressed: 'alignment == \'right\'', command: 'justifyright' }, + blockJustify:{ type: 'div', title: 'Block Justify', class: 'tinyeditor-control', faIcon: 'align-justify', backgroundPos: '34px -510px', pressed: 'alignment == \'justify\'', command: 'justifyfull' }, + orderedList:{ type: 'div', title: 'Insert Ordered List', class: 'tinyeditor-control', faIcon: 'list-ol', backgroundPos: '34px -300px', command: 'insertorderedlist' }, + unorderedList:{ type: 'div', title: 'Insert Unordered List', class: 'tinyeditor-control', faIcon: 'list-ul', backgroundPos: '34px -330px', command: 'insertunorderedlist' }, + outdent:{ type: 'div', title: 'Outdent', class: 'tinyeditor-control', faIcon: 'outdent', backgroundPos: '34px -360px', command: 'outdent' }, + indent:{ type: 'div', title: 'Indent', class: 'tinyeditor-control', faIcon: 'indent', backgroundPos: '34px -390px', command: 'indent' }, + removeFormatting:{ type: 'div', title: 'Remove Formatting', class: 'tinyeditor-control', faIcon: 'eraser', backgroundPos: '34px -720px', command: 'removeformat' }, + undo:{ type: 'div', title: 'Undo', class: 'tinyeditor-control', faIcon: 'undo', backgroundPos: '34px -540px', command: 'undo' }, + redo:{ type: 'div', title: 'Redo', class: 'tinyeditor-control', faIcon: 'repeat', backgroundPos: '34px -570px', command: 'redo' }, + fontColor:{ type: 'div', title: 'Font Color', class: 'tinyeditor-control', faIcon: 'font', backgroundPos: '34px -779px', specialCommand: 'showFontColors = !showFontColors', inner: '' }, + backgroundColor:{ type: 'div', title: 'Background Color', class: 'tinyeditor-control', faIcon: 'paint-brush', backgroundPos:'34px -808px', specialCommand: 'showBgColors = !showBgColors', inner: '' }, + image:{ type: 'div', title: 'Insert Image', class: 'tinyeditor-control', faIcon: 'picture-o', backgroundPos: '34px -600px', specialCommand: 'insertImage()' }, + hr:{ type: 'div', title: 'Insert Horizontal Rule', class: 'tinyeditor-control', faIcon: '-', backgroundPos: '34px -630px', command: 'inserthorizontalrule' }, + symbols:{ type: 'div', title: 'Insert Special Symbol', class: 'tinyeditor-control', faIcon: 'cny', backgroundPos: '34px -838px', specialCommand: 'showSpecChars = !showSpecChars', inner: '' }, + link:{ type: 'div', title: 'Insert Hyperlink', class: 'tinyeditor-control', faIcon: 'link', backgroundPos: '34px -660px', specialCommand: 'insertLink()' }, + unlink:{ type: 'div', title: 'Remove Hyperlink', class: 'tinyeditor-control', faIcon: 'chain-broken', backgroundPos: '34px -690px', command: 'unlink' }, + print:{ type: 'div', title: 'Print', class: 'tinyeditor-control', faIcon: 'print', backgroundPos: '34px -750px', command: 'print' }, + font:{ type: 'select', title: 'Font', class: 'tinyeditor-font', model: 'font', options: 'a as a for a in fonts', change: 'fontChange()' }, + size:{ type: 'select', title: 'Size', class: 'tinyeditor-size', model: 'fontsize', options: 'a.key as a.name for a in fontsizes', change: 'sizeChange()' }, + format:{ type: 'select', title: 'Style', class: 'tinyeditor-size', model: 'textstyle', options: 's.key as s.name for s in styles', change: 'styleChange()' } + }; + + var usingFontAwesome = scope.config && scope.config.fontAwesome; + + function getButtonHtml(button) { + var html = '<' + button.type; + html += ' class="' + button.class; + if (usingFontAwesome) { + html += ' tinyeditor-control-fa'; + } + html += '" '; + if (button.type == 'div') { + if (button.title) { + html += 'title="' + button.title + '" '; + } + if (button.backgroundPos && !usingFontAwesome) { + html += 'style="background-position: ' + button.backgroundPos + '; position: relative;" '; + } + if (button.pressed) { + html += 'ng-class="{\'pressed\': cursorStyle.' + button.pressed + '}" '; + } + if (button.command) { + var executable = '\'' + button.command + '\''; + if (button.commandParameter) { + executable += ', \'' + button.commandParameter + '\''; + } + html += 'ng-click="execCommand(' + executable + ')" '; + } else if (button.specialCommand) { + html += 'ng-click="' + button.specialCommand + '" '; + } + html += '>'; // this closes
    + if (button.faIcon && usingFontAwesome && button.faIcon != '-') { + html += ''; + } + if (button.faIcon && usingFontAwesome && button.faIcon == '-') { + html += '
    '; + } + if (button.inner) { + html+= button.inner; + } + } else if (button.type == 'select') { + html += 'ng-model="' + button.model + '" '; + html += 'ng-options="' + button.options + '" '; + html += 'ng-change="' + button.change + '" '; + html += ''; + } + html += ''; + return html; + } + + //show all panels by default + scope.toolbar = (scope.config && scope.config.toolbar)? scope.config.toolbar : [ + { name: 'basicStyling', items: ['bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript', 'leftAlign', 'centerAlign', 'rightAlign', 'blockJustify', '-'] }, + { name: 'paragraph', items: ['orderedList', 'unorderedList', 'outdent', 'indent', '-'] }, + { name: 'doers', items: ['removeFormatting', 'undo', 'redo', '-'] }, + { name: 'colors', items: ['fontColor', 'backgroundColor', '-'] }, + { name: 'links', items: ['image', 'hr', 'symbols', 'link', 'unlink', '-'] }, + { name: 'tools', items: ['print', '-'] }, + { name: 'styling', items: ['font', 'size', 'format'] } + ]; + //compile the template + var toolbarGroups = []; + angular.forEach(scope.toolbar, function(buttonGroup, index) { + var buttons = []; + angular.forEach(buttonGroup.items, function(button, index) { + var newButton = scope.panelButtons[button]; + if (!newButton) { + // checks if it is a button defined by the user + newButton = scope.config.buttons[button]; + } + this.push( getButtonHtml(newButton) ); + }, buttons); + this.push( + "
    " + + buttons.join('') + + "
    " + ); + }, toolbarGroups); + + var template = editorTemplate.replace('{toolbar}', toolbarGroups.join('')); + template = template.replace('{contentStyle}', attrs.contentStyle || ''); + //$element.replaceWith( angular.element($compile( editorTemplate.replace('{toolbar}', toolbarGroups.join('') ) )(scope)) ); + $element.html( template ); + $compile($element.contents())(scope); + + /* + * send the event to the iframe's controller to exec the command + */ + scope.execCommand = function(cmd, arg) { + //console.log('execCommand'); + //scope.$emit('execCommand', {command: cmd, arg: arg}); + switch(cmd) { + case 'bold': + scope.cursorStyle.bold = !scope.cursorStyle.bold; + break; + case 'italic': + scope.cursorStyle.italic = !scope.cursorStyle.italic; + break; + case 'underline': + scope.cursorStyle.underline = !scope.cursorStyle.underline; + break; + case 'strikethrough': + scope.cursorStyle.strikethrough = !scope.cursorStyle.strikethrough; + break; + case 'subscript': + scope.cursorStyle.sub = !scope.cursorStyle.sub; + break; + case 'superscript': + scope.cursorStyle.super = !scope.cursorStyle.super; + break; + case 'justifyleft': + scope.cursorStyle.alignment = 'left'; + break; + case 'justifycenter': + scope.cursorStyle.alignment = 'center'; + break; + case 'justifyright': + scope.cursorStyle.alignment = 'right'; + break; + case 'justifyfull': + scope.cursorStyle.alignment = 'justify'; + break; + } + //console.log(scope.cursorStyle); + scope.$broadcast('execCommand', {command: cmd, arg: arg}); + }; + + + scope.fonts = ['Verdana','Arial', 'Arial Black', 'Arial Narrow', 'Courier New', 'Century Gothic', 'Comic Sans MS', 'Georgia', 'Impact', 'Tahoma', 'Times', 'Times New Roman', 'Webdings','Trebuchet MS']; + /* + scope.$watch('font', function(newValue) { + if(newValue) { + scope.execCommand( 'fontname', newValue ); + scope.font = ''; + } + }); + */ + scope.fontChange = function() { + scope.execCommand( 'fontname', scope.font ); + //scope.font = ''; + }; + scope.fontsizes = [{key: 1, name: 'x-small'}, {key: 2, name: 'small'}, {key: 3, name: 'normal'}, {key: 4, name: 'large'}, {key: 5, name: 'x-large'}, {key: 6, name: 'xx-large'}, {key: 7, name: 'xxx-large'}]; + scope.mapFontSize = { 10: 1, 13: 2, 16: 3, 18: 4, 24: 5, 32: 6, 48: 7}; + scope.sizeChange = function() { + scope.execCommand( 'fontsize', scope.fontsize ); + }; + /* + scope.$watch('fontsize', function(newValue) { + if(newValue) { + scope.execCommand( 'fontsize', newValue ); + scope.fontsize = ''; + } + }); + */ + scope.styles = [{name: 'Paragraph', key: '

    '}, {name: 'Header 1', key: '

    '}, {name: 'Header 2', key: '

    '}, {name: 'Header 3', key: '

    '}, {name: 'Header 4', key: '

    '}, {name: 'Header 5', key: '

    '}, {name: 'Header 6', key: '
    '}]; + scope.styleChange = function() { + scope.execCommand( 'formatblock', scope.textstyle ); + }; + /* + scope.$watch('textstyle', function(newValue) { + if(newValue) { + scope.execCommand( 'formatblock', newValue ); + scope.fontsize = ''; + } + }); + */ + scope.showFontColors = false; + scope.setFontColor = function( color ) { + scope.execCommand('foreColor', color); + }; + scope.showBgColors = false; + scope.setBgColor = function( color ) { + scope.execCommand('hiliteColor', color); + }; + + scope.showSpecChars = false; + scope.insertSpecChar = function(symbol) { + insertElement(symbol); + }; + scope.insertLink = function() { + loadVars(); + if (iframeWindow.getSelection().focusNode == null) return; // user should at least click the editor + var elementBeingEdited = ngpUtils.getSelectionBoundaryElement(iframeWindow, true); + var defaultUrl = 'http://'; + if (elementBeingEdited && elementBeingEdited.nodeName == 'A') { + defaultUrl = elementBeingEdited.href; + + // now we select the whole a tag since it makes no sense to add a link inside another link + var selectRange = iframeDocument.createRange(); + selectRange.setStart(elementBeingEdited.firstChild, 0); + selectRange.setEnd(elementBeingEdited.firstChild, elementBeingEdited.firstChild.length); + var selection = iframeWindow.getSelection(); + selection.removeAllRanges(); + selection.addRange(selectRange); + } + var val; + if(scope.api && scope.api.insertLink && angular.isFunction(scope.api.insertLink)) { + val = scope.api.insertLink.apply( scope.api.scope || null, [defaultUrl]); + } else { + val = prompt('Please enter the URL', 'http://'); + } + //resolve the promise if any + $q.when(val).then(function(data) { + scope.execCommand('createlink', data); + }); + }; + /* + * insert + */ + scope.insertImage = function() { + var val; + if(scope.api && scope.api.insertImage && angular.isFunction(scope.api.insertImage)) { + val = scope.api.insertImage.apply( scope.api.scope || null ); + } + else { + val = prompt('Please enter the picture URL', 'http://'); + val = ''; //we convert into HTML element. + } + //resolve the promise if any + $q.when(val).then(function(data) { + insertElement(data); + }); + }; + $element.ready(function() { + function makeUnselectable(node) { + if (node.nodeType == 1) { + node.setAttribute("unselectable", "on"); + node.unselectable = 'on'; + } + var child = node.firstChild; + while (child) { + makeUnselectable(child); + child = child.nextSibling; + } + } + //IE fix + for(var i = 0; i < document.getElementsByClassName('tinyeditor-header').length; i += 1) { + makeUnselectable(document.getElementsByClassName("tinyeditor-header")[i]); + } + }); + //catch the cursort position style + scope.$on('cursor-position', function(event, data) { + //console.log('cursor-position', data); + scope.cursorStyle = data; + scope.font = data.font.replace(/(')/g, ''); //''' replace single quotes + scope.fontsize = scope.mapFontSize[data.size]? scope.mapFontSize[data.size] : 0; + }); + }; + return { + link: linker, + scope: { + content: '=', //this is our content which we want to edit + api: '=', //this is our api object + config: '=' + }, + restrict: 'AE', + replace: true + } + } +]); +angular.module('ngWYSIWYG').directive('ngpContentFrame', ['ngpImageResizer', 'ngpUtils', 'NGP_EVENTS', '$compile', + '$timeout', '$sanitize', function(ngpImageResizer, ngpUtils, NGP_EVENTS, $compile, $timeout, $sanitize) { + + //kudos http://stackoverflow.com/questions/13881834/bind-angular-cross-iframes-possible + var linker = function( scope, $element, attrs, ctrl ) { + var $document = $element[0].contentDocument; + $document.open(); //damn Firefox. kudos: http://stackoverflow.com/questions/15036514/why-can-i-not-set-innerhtml-of-an-iframe-body-in-firefox + $document.write(''); + $document.close(); + $document.designMode = 'On'; + ngpImageResizer.setup(scope, $document); + var $body = angular.element($element[0].contentDocument.body); + var $head = angular.element($element[0].contentDocument.head); + $head.append(''); + $body.attr('contenteditable', 'true'); + //Raza change + $body.focus(); + // fixing issue that makes caret disappear on chrome (https://github.com/psergus/ngWYSIWYG/issues/22) + $document.addEventListener('click', function(event) { + if (event.target.tagName === 'HTML') { + event.target.querySelector('body').focus(); + } + scope.$emit(NGP_EVENTS.ELEMENT_CLICKED, event.target); + }); + + // this option enables you to specify a custom CSS to be used within the editor (the editable area) + + + if (attrs.contentStyle) { + $head.append(''); + } + + //model --> view + ctrl.$render = function() { + //sanitize the input only if defined through config + $body[0].innerHTML = ctrl.$viewValue? ( (scope.config && scope.config.sanitize)? $sanitize(ctrl.$viewValue) : ctrl.$viewValue) : ''; + }; + + scope.sync = function() { + scope.$evalAsync(function(scope) { + ctrl.$setViewValue($body.html()); + }); + }; + + var debounce = null; //we will debounce the event in case of the rapid movement. Overall, we are intereseted in the last cursor/caret position + //view --> model + $body.bind('click keyup change paste', function() { //we removed 'blur' event + //lets debounce it + if(debounce) { + $timeout.cancel(debounce); + } + debounce = $timeout(function blurkeyup() { + var contentDocument = $body[0].ownerDocument; + var imageResizer = contentDocument.querySelector('.ngp-image-resizer'); + var html = $body[0].innerHTML; + if (imageResizer) { + html = html.replace(imageResizer.outerHTML, ''); + } + ctrl.$setViewValue(html); + //check the caret position + //http://stackoverflow.com/questions/14546568/get-parent-element-of-caret-in-iframe-design-mode + var el = ngpUtils.getSelectionBoundaryElement($element[0].contentWindow, true); + if(el) { + var computedStyle = $element[0].contentWindow.getComputedStyle(el); + var elementStyle = { + 'bold': (computedStyle.getPropertyValue("font-weight") == 'bold' || parseInt(computedStyle.getPropertyValue("font-weight")) >= 700), + 'italic': (computedStyle.getPropertyValue("font-style") == 'italic'), + 'underline': (computedStyle.getPropertyValue("text-decoration") == 'underline'), + 'strikethrough': (computedStyle.getPropertyValue("text-decoration") == 'line-through'), + 'font': computedStyle.getPropertyValue("font-family"), + 'size': parseInt(computedStyle.getPropertyValue("font-size")), + 'color': computedStyle.getPropertyValue("color"), + 'sub': (computedStyle.getPropertyValue("vertical-align") == 'sub'), + 'super': (computedStyle.getPropertyValue("vertical-align") == 'super'), + 'background': computedStyle.getPropertyValue("background-color"), + 'alignment': computedStyle.getPropertyValue("text-align") + }; + //dispatch upward the through the scope chain + scope.$emit('cursor-position', elementStyle); + //console.log( JSON.stringify(elementStyle) ); + } + }, + 100/*ms*/, true /*invoke apply*/); + }); + + + scope.range = null; + scope.getSelection = function() { + if($document.getSelection) { + var sel = $document.getSelection(); + if(sel.getRangeAt && sel.rangeCount) { + scope.range = sel.getRangeAt(0); + } + } + }; + scope.restoreSelection = function() { + if(scope.range && $document.getSelection) { + var sel = $document.getSelection(); + sel.removeAllRanges(); + sel.addRange(scope.range); + } + }; + + scope.$on('execCommand', function(e, cmd) { + console.log('execCommand: '); + console.log(cmd); + $element[0].contentDocument.body.focus(); + //scope.getSelection(); + var sel = $document.selection; //http://stackoverflow.com/questions/11329982/how-refocus-when-insert-image-in-contenteditable-divs-in-ie + if (sel) { + var textRange = sel.createRange(); + $document.execCommand(cmd.command, 0, cmd.arg); + textRange.collapse(false); + textRange.select(); + } + else { + $document.execCommand(cmd.command, 0, cmd.arg); + } + //scope.restoreSelection(); + $document.body.focus(); + scope.sync(); + }); + + scope.$on('insertElement', function(event, html) { + var sel, range; + if ($document.defaultView.getSelection) { + sel = $document.defaultView.getSelection(); + if (sel.getRangeAt && sel.rangeCount) { + range = sel.getRangeAt(0); + range.deleteContents(); + + // Range.createContextualFragment() would be useful here but is + // only relatively recently standardized and is not supported in + // some browsers (IE9, for one) + var el = $document.createElement("div"); + el.innerHTML = html; + var frag = $document.createDocumentFragment(), node, lastNode; + while ((node = el.firstChild)) { + lastNode = frag.appendChild(node); + } + var firstNode = frag.firstChild; + range.insertNode(frag); + + // Preserve the selection + if (lastNode) { + range = range.cloneRange(); + range.setStartAfter(lastNode); + range.collapse(true); + sel.removeAllRanges(); + sel.addRange(range); + } + } + } else if ($document.selection && $document.selection.type != "Control") { + // IE < 9 + $document.selection.createRange().pasteHTML(html); + } + scope.sync(); + }); + + scope.$on('$destroy', function() { + //clean after myself + + }); + + //init + try { + $document.execCommand("styleWithCSS", 0, 0); // <-- want the Old Schoold elements like or , comment this line. kudos to: http://stackoverflow.com/questions/3088993/webkit-stylewithcss-contenteditable-not-working + $document.execCommand('enableObjectResizing', false, 'false'); + $document.execCommand('contentReadOnly', 0, 'false'); + } + catch(e) { + try { + $document.execCommand("useCSS", 0, 1); + } + catch(e) { + } + } + }; + return { + link: linker, + require: 'ngModel', + scope: { + config: '=ngpContentFrame' + }, + replace: true, + restrict: 'AE' + } + } +]); +angular.module('ngWYSIWYG').directive('ngpResizable', ['$document', function($document) { + return function($scope, $element) { + var doc = $document[0]; + var element = $element[0]; + + var resizeButton = doc.createElement('span'); + resizeButton.className = 'resizer'; + element.appendChild(resizeButton); + + resizeButton.addEventListener('mousedown', function() { + doc.addEventListener('mousemove', resize); + doc.addEventListener('mouseup', stopResizing); + + var iframes = doc.querySelectorAll('iframe'); + for (var i = 0; i < iframes.length; i++) { + iframes[i].contentWindow.document.addEventListener('mouseup', stopResizing); + iframes[i].contentWindow.document.addEventListener('mousemove', resize); + } + + function resize(event) { + event.preventDefault(); + + // Function to manage resize down event + var cursorVerticalPosition = event.pageY; + if (event.view != doc.defaultView) { + // we are hover our iframe + cursorVerticalPosition = event.pageY + + event.view.frameElement.getBoundingClientRect().top + + doc.defaultView.pageYOffset; + } + var height = cursorVerticalPosition - (element.getBoundingClientRect().top + doc.defaultView.pageYOffset); + + var currentHeight = element.style.height.replace('px', ''); + if (currentHeight && currentHeight < height && + window.innerHeight - event.clientY <= 45) { + // scrolling to improve resize usability + doc.defaultView.scrollBy(0, height - currentHeight); + } + + element.style.height = height + 'px'; + } + + function stopResizing() { + doc.removeEventListener('mousemove', resize); + doc.removeEventListener('mouseup', stopResizing); + + var iframes = doc.querySelectorAll('iframe'); + for (var i = 0; i < iframes.length; i++) { + iframes[i].contentWindow.document.removeEventListener('mouseup', stopResizing); + iframes[i].contentWindow.document.removeEventListener('mousemove', resize); + } + } + }); + }; +}]); +angular.module('ngWYSIWYG').service('ngpUtils', [function() { + var service = this; + + service.getSelectionBoundaryElement = function(win, isStart) { + var range, sel, container = null; + var doc = win.document; + if (doc.selection) { + // IE branch + range = doc.selection.createRange(); + range.collapse(isStart); + return range.parentElement(); + } + else if (doc.getSelection) { + //firefox + sel = doc.getSelection(); + if (sel.rangeCount > 0) { + range = sel.getRangeAt(0); + //console.log(range); + container = range[isStart ? "startContainer" : "endContainer"]; + if (container.nodeType === 3) { + container = container.parentNode; + } + //console.log(container); + } + } + else if (win.getSelection) { + // Other browsers + sel = win.getSelection(); + if (sel.rangeCount > 0) { + range = sel.getRangeAt(0); + container = range[isStart ? "startContainer" : "endContainer"]; + + // Check if the container is a text node and return its parent if so + if (container.nodeType === 3) { + container = container.parentNode; + } + } + } + return container; + }; +}]); +return 'ngWYSIWYG'; +}));