I'm currently upgrading a WYSIWYG Rich Text Editor that was based on the DHTML Editor Control (DEC) to use the more modern editor controls in modern browsers. I'm using an iFrame with design mode turned on and a mixture of regular javascript and jquery.
One of my requirements is to insert html content (forms etc) into the iframe so that users can edit them. I have it working in FF + Chrome, but IE is proving a pain. My current code inserts the content at the start of the parent document and not the iframes, I'm using the selection.createRange() function that when used with DEC would insert the content either at the cursor if the control was selected or at the end of the document inside the editor if not.
Currently it only works when I select some text in IE. Heres my current code (apologies if it looks unformatted the firewall at work is blocking a lot of the css + js from stackoverflow), any ideas?
<html>
<head>
<title>Text Editor Test</title>
<style type="text/css">
.toolbar {background-color:#BFC193;width:500px;padding:5px;}
#insertForm {position: absolute;height:60px;width:200px;top:50px;left:50px;border:1pt solid black;background-color:#fff;padding:10px;}
</style>
</head>
<body>
<h1>MSHTML Text Editor</h1>
<form id="frmEdit">
<div class="toolbar" id="toolbar">
<input type="button" name="insertHTML" value="insert html" onClick="showForm();"/>
</div>
<div id="insertForm" style="display:none;">
Insert Content Form
<input type="button" value="OK" style="width: 80px" onClick="insertContent();">
</div>
<script type="text/javascript" src="jquery-1.6.4.js"></script>
<script type="text/javascript">
// functions to execute once the DOM has loaded.
$(document).ready(function() {
pageInit();
});
function pageInit() {
// create iframe
$('.toolbar').after("<iframe id='frameEdit' style='width:500px; height:400px' ></iframe>");
//insert delay for firefox + webkit browsers before turning on designMode open + close seems to do the job
document.getElementById('frameEdit').contentWindow.document.open();
document.getElementById('frameEdit').contentWindow.document.close();
document.getElementById('frameEdit').contentWindow.document.designMode='On';
}
function showForm() {
$('#insertForm').toggle();
}
function insertContent() {
// turn off form
showForm();
// set test content
var htmlContent = "<p>Insert Test</p>";
var doc = document.getElementById('frameEdit').contentWindow.document;
if (doc.selection && doc.selection.createRange) { // IE
var range = doc.selection.createRange();
range.pasteHTML(htmlContent);
} else { // FF
doc.execCommand('insertHTML', false, htmlContent);
}
}
</script>
</form>
</body>
</html>
Make your button unselectable to stop it nicking the focus from the iframe. You can do this in IE using uneselectable="on":
<input type="button" value="OK" unselectable="on"
style="width: 80px" onclick="insertContent();">
Related
I want to convert HTML to PDF with the click of a button and download.
My js working perfectly only need the latest JavaScript CDN link.
HTML
<div id="pageprint">
<div id="reportbox">Hello World!!</div>
</div>
<button type="button" onclick="downloadCode();">Download HTML</button>
Javascript
<script>
function generatePDF() {
const element = document.getElementById("pageprint");
document.getElementById("reportbox").style.display = "block";
document.getElementById("reportbox").style.marginTop = "0px";
document.getElementById("pageprint").style.border = "1px solid black";
html2pdf().from(element).save('download.pdf');
}
function downloadCode(){
var x = document.getElementById("reportbox");
generatePDF();
setTimeout(function() { window.location=window.location;},3000);}
</script>
If all you need is the CDN then simply add it after the </body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js" integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
function generatePDF() {
const element = document.getElementById("pageprint");
document.getElementById("reportbox").style.display = "block";
document.getElementById("reportbox").style.marginTop = "0px";
document.getElementById("pageprint").style.border = "1px solid black";
html2pdf().from(element).save('download.pdf');
}
function downloadCode(){
var x = document.getElementById("reportbox");
generatePDF();
setTimeout(function() { window.location=window.location;},3000);}
<div id="pageprint">
<div id="reportbox">Hello World!!</div>
</div>
<button type="button" onclick="downloadCode();">Download HTML</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
However seems a very odd way to ask a user to download a pdf page since the option disappears after the download is attempted, so change of mind does not keep it user visible to try differently on fail.
So for example, I say open the download on current page, I see
but if I say open in PDF Viewer I see
It's much simpler to layout the printable HTML page as text not image, and suggest the user prints or saves exactly as their browser is configured and their desire, best result for all, especially as no libraries are needed.
Nor will the page be cluttered by buttons.
You can print html just as follow.
<style type="text/css">
#media print{ button {display:none} };
</style>
<div id="pageprint">
<div id="reportbox">Hello World!!</div>
</div>
<button type="button" onclick=javascript:window.print()>Download HTML</button>
Please let me know if any issue found
There isn't an easy way to do this. The best thing you could do is to open an empty page, fill it with your html data and print it to pdf. Or look for some external libary like jsPDF.
example for print to pdf:
var wnd = window.open('about:blank', '', '_blank');
wnd.document.write("<p> Some HTML-Content </p> ");
wnd.print();
I've been through a lot of tutorials and I can never get this to work:
I want to save the content of a div (with contenteditable enabled) to a .txt file with node webkit. That part looks like this:
<div id="editor" class="textbox" contenteditable></div>
And I have the input field that allows me to select the file:
<input type="file" nwsaveas="untitled.txt" style="display:none;"/>
However I can't find any resources on how to save the value of the editor div as a .txt file on the user's computer.
I tried this tuts plus tutorial that briefly explains it however it didn't seem to work when I tried it on my own project: http://code.tutsplus.com/tutorials/introduction-to-html5-desktop-apps-with-node-webkit--net-36296
Does anyone know how I can achieve this?
You have to make file dialog open with emulating click event of an input, then get innerHTML of #editor, and finally use node's fs.writeFile to save content.
Here is full working example:
<!DOCTYPE html>
<html>
<head>
<script>
var initInputFile = function() {
document.getElementById('inputFile').addEventListener('change', function() {
var path = this.value; //get fullpath of chosen file
var content = document.getElementById('editor').innerHTML; //get editor's content
content = (' ' + content).slice(1); //hack to prevent strange bug of saving just half of the content
require('fs').writeFile(path, content, function(err) {
if (err) throw err;
console.log('done');
});
var wrapper = document.getElementById('inputFileWrapper');
wrapper.innerHTML = wrapper.innerHTML; //hack to make "change" event trigger...
initInputFile(); //...when choosing the same file
});
}
window.onload = function() {
initInputFile();
document.getElementById('saveBtn').addEventListener('click', function() {
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click');
document.getElementById('inputFile').dispatchEvent(event);
});
}
</script>
</head>
<body>
<div id="editor" class="textbox" style="width:400px; height:100px;" contenteditable></div>
<div id="inputFileWrapper" style="display:none;">
<input type="file" id="inputFile" nwsaveas="untitled.txt"/>
</div>
<input type="button" id="saveBtn" value="Save" />
</body>
</html>
Hope this helps.
I am using a code snippet that I found to display a multipage form using visibility hidden.
There is a very good possibility that all of my problem stems from this method. That resource was from here:
http://www.devx.com/webdev/Article/10483/0/page/2
It is a fairly straightforward way to display multiple pages of a form...it probably was never intended to be able to allow printing.
<html>
<head>
<script type="text/javascript" src="jquery-1.10.2.min.js"></script>
<script language="JavaScript">
$.getScript("printThis.js", function(){
});
var currentLayer = 'page1';
function showLayer(lyr){
hideLayer(currentLayer);
document.getElementById(lyr).style.visibility = 'visible';
currentLayer = lyr;
}
function hideLayer(lyr){
document.getElementById(lyr).style.visibility = 'hidden';
}
function showValues(form){
var values = '';
var len = form.length - 1; //Leave off Submit Button
for(i=0; i<len; i++){
if(form[i].id.indexOf("C")!=-1||form[i].id.indexOf("B")!=-1)
continue;
values += form[i].id;
values += ': ';
values += form[i].value;
values += '\n';
}
alert(values);
}
</script>
<style>
body{
font: 10pt sans-serif;
}
.page{
position: absolute;
top: 10;
left: 100;
visibility: hidden;
}
</style>
</head>
<body>
<form id="multiForm" action="App1.php" method="POST" action="javascript:void(0)" onSubmit="showValues(this)" id="app">
<div id="page1" class="page" style="visibility:visible;">
Applicant Name: <input type="text" size="50" name="name1" >
</form>
<p><input type="button" id="C1" value="Continue" onClick="showLayer('page2')"></p>
</div>
<div id="page2" class="page">
This is Page 2
<br>
<input type="button" id="B1" value="Go Back" onClick="showLayer('page1')">
<input type="button" id="B2" value="Print App" onClick="$('#page1').printThis({})">
<br><br>
</div>
</form>
</body>
</html>
The "Print App" button is properly calling the printThis plugin. However, I get no content from the page1 DIV section. All that is printed is the normal header portion (Page 1 of 1) in the upper right and about:blank in lower left and date in lower right of pageā¦no content, which with my sample file should be Applicant Name input box.
I assume that this is because the DIV for page1 is set to "hidden" while the content of page2 is being displayed. If I substitute "page2" in the button call then I get the content from page2 as expected.
So...I guess what I am after is a way to temporarily change the DIV being referenced in the printThis button call to be visible just long enough to perform the page print.
Any ideas?
I'm the plugin author - you need to incorporate the print media query into your css.
This would also help users that select file > print or control + P, as it will show all form elements.
The print media query allows you to make styling changes specifically for the printed page.
Example:
#media print {
#page1, #page2 {
display: block;
visibility: visible;
position: relative;
}
}
You include this in your css.
Additionally, based on your above code - you have css and javascript inline in your page. You should consider moving both to an external files, for maintenance and improved code standards.
printThis won't work with your current setup, because the plugin looks for the container (selector) you have specified and any linked css in the head of the document.
So for the above, you can do the following:
<!-- move all of this to the bottom of the page for performance -->
<script type="text/javascript" src="jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="printThis.js"></script>
<script type="text/javascript" src="myJavascript.js"></script>
<!-- the above file is your javascript externalized, remove $.getScript, wrap in $(document).ready() -->
Then put this in your head:
<link type='text/css' rel='stylesheet' href='style.css'>
<!-- contains your css from the page, including the print media query -->
I was reading the "HTML animation library sample" http://code.msdn.microsoft.com/windowsapps/Using-the-Animation-787f3720 and notice that none of the element variable in the javascript are declared.
Where is the variable declaration or getelement for target1, target2, etc? Note the strict declaration of the top. This is the sample code from Microsoft.
From pointerFeedback.js
(function () {
"use strict";
var page = WinJS.UI.Pages.define("/html/pointerFeedback.html", {
ready: function (element, options) {
target1.addEventListener("MSPointerDown", onPointerDown, false);
target1.addEventListener("MSPointerUp", onPointerUp, false);
target2.addEventListener("MSPointerDown", onPointerDown, false);
target2.addEventListener("MSPointerUp", onPointerUp, false);
target3.addEventListener("MSPointerDown", onPointerDown, false);
target3.addEventListener("MSPointerUp", onPointerUp, false);
}
});
function onPointerDown(evt) {
WinJS.UI.Animation.pointerDown(evt.srcElement);
}
function onPointerUp(evt) {
WinJS.UI.Animation.pointerUp(evt.srcElement);
}
})();
From pointerFeedback.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link rel="stylesheet" href="/css/pointerFeedback.css" />
<script src="/js/pointerFeedback.js"></script>
</head>
<body>
<div id="input" data-win-control="SdkSample.ScenarioInput">
<p>Use the Pointer Up and Pointer Down animations to
show tap and click feedback on elements. These animations should be used on elements
that can be interacted with and can trigger actions. They should not be used on
elements that are disabled or on standard web controls that already have other feedback, such as buttons.</p>
</div>
<div id="output" data-win-control="SdkSample.ScenarioOutput">
<div class="example">
<h3>Applied to different sized elements:</h3>
<p>Click or tap on the boxes below to see the animation.</p>
<div id="target1" class="large">
200 pixels
</div>
<div id="target2" class="medium">
100 pixels
</div>
<div id="target3" class="small">
50 pixels
</div>
</div>
</div>
</body>
</html>
Very interesting question! I suspect that every element with a specified 'id' will be automatically made available to JavaScript.
I could confirm this by creating a new project based on the "Blank App" template and adding a new div with id="foobar". Then I started the app and switched to the JavaScript console and voila: a 'foobar' variable was available!
After doing Win8 app development for some months, I never stumbled over this, well done!
I'd like to be able to convert specific textareas on a page to be ACE editors.
Does anyone have any pointers please?
EDIT:
I have the the editor.html file working with one textarea, but as soon as I add a second, the second isn't converted to an editor.
EDIT 2:
I decided to scrap the idea of having several, and instead open one up in a new window. My new predicament is that when I hide() and show() the textarea, the display goes awry. Any ideas?
As far as I understood the idea of Ace, you shouldn't make a textarea an Ace editor itself. You should create an additional div and update textarea using .getSession() function instead.
html
<textarea name="description"/>
<div id="description"/>
js
var editor = ace.edit("description");
var textarea = $('textarea[name="description"]').hide();
editor.getSession().setValue(textarea.val());
editor.getSession().on('change', function(){
textarea.val(editor.getSession().getValue());
});
or just call
textarea.val(editor.getSession().getValue());
only when you submit the form with the given textarea. I'm not sure whether this is the right way to use Ace, but it's the way it is used on GitHub.
Duncansmart has a pretty awesome solution on his github page, progressive-ace which demonstrates one simple way to hook up an ACE editor to your page.
Basically we get all <textarea> elements with the data-editor attribute and convert each to an ACE editor. The example also sets some properties which you should customize to your liking, and demonstrates how you can use data attributes to set properties per element like showing and hiding the gutter with data-gutter.
// Hook up ACE editor to all textareas with data-editor attribute
$(function() {
$('textarea[data-editor]').each(function() {
var textarea = $(this);
var mode = textarea.data('editor');
var editDiv = $('<div>', {
position: 'absolute',
width: textarea.width(),
height: textarea.height(),
'class': textarea.attr('class')
}).insertBefore(textarea);
textarea.css('display', 'none');
var editor = ace.edit(editDiv[0]);
editor.renderer.setShowGutter(textarea.data('gutter'));
editor.getSession().setValue(textarea.val());
editor.getSession().setMode("ace/mode/" + mode);
editor.setTheme("ace/theme/idle_fingers");
// copy back to textarea on form submit...
textarea.closest('form').submit(function() {
textarea.val(editor.getSession().getValue());
})
});
});
textarea {
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js"></script>
<textarea name="my-xml-editor" data-editor="xml" data-gutter="1" rows="15"></textarea>
<br>
<textarea name="my-markdown-editor" data-editor="markdown" data-gutter="0" rows="15"></textarea>
You can have multiple Ace Editors. Just give each textarea an ID and create an Ace Editor for both IDS like so:
<style>
#editor, #editor2 {
position: absolute;
width: 600px;
height: 400px;
}
</style>
<div style="position:relative; height: 450px; " >
<div id="editor">some text</div>
</div>
<div style="position:relative; height: 450px; " >
<div id="editor2">some text</div>
</div>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="theme-twilight.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-xml.js" type="text/javascript" charset="utf-8"></script>
<script>
window.onload = function() {
var editor = ace.edit("editor");
editor.setTheme("ace/theme/twilight");
var XmlMode = require("ace/mode/xml").Mode;
editor.getSession().setMode(new XmlMode());
var editor2 = ace.edit("editor2");
editor2.setTheme("ace/theme/twilight");
editor2.getSession().setMode(new XmlMode());
};
</script>
To create an editor just do:
HTML:
<textarea id="code1"></textarea>
<textarea id="code2"></textarea>
JS:
var editor1 = ace.edit('code1');
var editor2 = ace.edit('code2');
editor1.getSession().setValue("this text will be in the first editor");
editor2.getSession().setValue("and this in the second");
CSS:
#code1, code2 {
position: absolute;
width: 400px;
height: 50px;
}
They must be explicitly positioned and sized. By show() and hide() I believe you are referring to the jQuery functions. I'm not sure exactly how they do it, but it cannot modify the space it takes up in the DOM. I hide and show using:
$('#code1').css('visibility', 'visible');
$('#code2').css('visibility', 'hidden');
If you use the css property 'display' it will not work.
Check out the wiki here for how to add themes, modes, etc... https://github.com/ajaxorg/ace/wiki/Embedding---API
Note: they do not have to be textareas, they can be whatever element you want.
For anyone that just wants a minimal, working example of using Ace from the CDN:
<!DOCTYPE html>
<html lang="en">
<body style="margin:0">
<div id="editor">function () {
console.log('this is a demo, try typing!')
}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.01/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/javascript");
document.getElementById("editor").style.height = "120px";
</script>
</body>
</html>