I am using IE11 for this since that is my companys standard browser.
I am working on a solution to catch the paste event when pasting a screen dump into the web application. So far so good but after I have pasted the image I would like to change the size. Preferable before actually so I don't get a jumping application.
I have create a jsfiddle where you can see the entire test application: http://jsfiddle.net/e5f5gLan/3/
Do like this when running the jsfiddle:
Make a screen shot
Put the marker in the red square
Press Ctrl + v
Now the intention is that the pasted image should become 100px x 100px in size. It doesn't.
The problem is that I am not getting hold of the DOM object so I can set the style/size of the image.
The significant part is at the end of the javascript (I guess...):
var image_container = document.getElementById('pastearea');
var image = image_container.getElementsByTagName("img");
image[0].setAttribute("style", "width: 100px; height: 100px");
First of all, I imagined that the img element would become part of an array and that I should access the only img-element using image[0]. But then I get the error "Not possible to get setAttribute for a reference that is undefined or null. " (freely translated from Swedish...)
Ok, perhaps it understands it is only one element and just returns an object that isn't an array. So I changed the last row above to:
image.setAttribute("style", "width: 100px; height: 100px");
Then I get that setAttribute is not supported by the object.
If I create an HTML page with similar structure (img inside div) and just tries to change the size, then it works. Check out this one (click the button to shrink the image): http://jsfiddle.net/m4kzd7jp/3/
How can I change change the size of the image before or after I have pasted it?
Yeah I added the style and it worked to keep it 100px height/width.
#pastearea img { width: 100px; height: 100px; }
I tested your code and the filelist was always 0. If you want to handle this through code, here's what I was using in our project. basically you should be looping items not files and checking if it is a file.
function (e) {
var clip = e.clipboardData || window.clipboardData;
if (clip) {
var preInsert = getData(); // this pulled the input box text or div html
for (var i = 0; i < clip.items.length; i++) {
var item = clip.items[i];
if (item.kind == "file" &&
item.type.indexOf('image/') !== -1) {
var fr = new FileReader();
fr.onloadend = function () {
if (preInsert == getData()) // if nothing changed, we'll handle it otherwise it was already handled
setData(fr.result);
};
var data = item.getAsFile();
fr.readAsDataURL(data);
}
}
}
}
This area shows what I was doing in angular to get/set
var getSelection = function() {
if (window.getSelection && window.getSelection().rangeCount > 0) //FF,Chrome,Opera,Safari,IE9+
{
return window.getSelection().getRangeAt(0).cloneRange();
}
else if (document.selection)//IE 8 and lower
{
return document.selection.createRange();
}
return null;
}
var isInputTextarea = false; // I was being more generic in my angular directive
var getData = function () {
if (isInputTextarea)
return $element.val();
return $element.html();
};
var setData = function (src) {
if (isInputTextarea) {
$element.val(function (index, value) {
return value + " " + "<img src='" + src + "'>";
});
}
else {
var selection = getSelection(); // I presume we have focus at this point, since it is a paste event
if (selection) {
var img = document.createElement("img");
img.src = src;
selection.insertNode(img);
}
}
};
Related
The new predictive type feature Smart Compose of Gmail is quite interesting.
Let's say we want to implement such a functionality ourselves:
User enters beginning of text, e.g. How and in gray behind it appears are you?.
User hits TAB and the word tomorrow is set.
Example:
Can a textarea with Javascript be used to achieve this?
And if not, how could this be implemented otherwise?
My previous answer got deleted, so here's a better attempt at explaining how I've somewhat replicated Smart Compose. My answer only focuses on the pertinent aspects. See https://github.com/jkhaui/predictable for the code.
We are using vanilla js and contenteditable in our solution (just like Gmail does). I bootstrap my example with create-react-app and Medium-Editor, but neither React nor Medium-Editor are necessary.
We have a database of "suggestions" which can be an array of words or phrases. For our purposes, in my example, I use a static array containing 50,000+ common English phrases. But you can easily see how this could be substituted for a dynamic data-source - such as how Gmail uses its neural network API to offer suggestions based on the current context of users' emails: https://ai.googleblog.com/2018/05/smart-compose-using-neural-networks-to.html
Smart Compose uses JavaScript to insert a <span></span> element immediately after the word you are writing when it detects a phrase to suggest. The span element contains only the characters of the suggestion that have not been typed.
E.g. Say you've written "Hi, how a" and a suggestion appears. Let's say the entire suggestion is "how are you going today". In this case, the suggestion is rendered as "re you going today" within the span. If you continue typing the characters in the placeholder - such as "Hi, how are you goi" - then the text content of the span changes dynamically - such that "ng today" is now the text within the span.
My solution works slightly differently but achieves the same visual effect. The difference is I can't figure out how to insert an inline span adjacent to the user's current text and dynamically mutate the span's content in response to the user's input.
So, Instead, I've opted for an overlay element containing the suggestion. The trick is now to position the overlay container exactly over the last word being typed (where the suggestion will be rendered). This provides the same visual effect of an inline typeahead suggestion.
We achieve correct positioning of the overlay by calculating the top + left coordinates for the last word being typed. Then, using JavaScript, we couple the top + left CSS attributes of the overlay container so that they always match the coordinates of the last word. The tricky part is getting these coordinates in the first place. The general steps are:
Call window.getSelection().anchorNode.data.length which retrieves the current text node the user is writing in and returns its length, which is necessary to calculate the offset of the last word within its parent element (explained in the following steps).
For simplicity's sake, only continue if the caret is at the end of the text.
Get the parent node of the current text node we're in. Then get the length of the parent node's text content.
The parent node's text length - the current text node's (i.e the last word's) text length = the offset position of the last text node within its contenteditable parent.
Now we have the offset of the last word, we can use the various range methods to insert a span element immediately preceding the last word: https://developer.mozilla.org/en-US/docs/Web/API/Range
Let's call this span element a shadowNode. Mentally, you can now picture the DOM as follows: we have the user's text content, and we have a shadowNode placed at the position of the last word.
Finally, we call getBoundingClientRect on the shadowNode which returns specific metadata, including the top + left coordinates we're after.
Apply the top + left coordinates to the suggestions overlay container and add the appropriate event handlers/listeners to render the suggestion when Tab is pressed.
Visit this link for documentation https://linkkaro.com/autocomplete.html .
May be you need to make few adjustment in CSS ( padding and width ).
I hope it will help.[![
$(document).ready(function(){
//dummy random output. You can use api
var example = {
1:"dummy text 1",
2:"dummy text 2"
};
function randomobj(obj) {
var objkeys = Object.keys(obj)
return objkeys[Math.floor(Math.random() * objkeys.length)]
}
var autocomplete = document.querySelectorAll("#autocomplete");
var mainInput = document.querySelectorAll("#mainInput");
var foundName = '';
var predicted = '';
var apibusy= false;
var mlresponsebusy = false;
$('#mainInput').keyup(function(e) {
//check if null value send
if (mainInput[0].value == '') {
autocomplete[0].textContent = '';
return;
}
//check if space key press
if (e.keyCode == 32) {
CallMLDataSetAPI(e);
scrolltobototm();
return;
}
//check if Backspace key press
if (e.key == 'Backspace'){
autocomplete[0].textContent = '';
predicted = '';
apibusy = true;
return;
}
//check if ArrowRight or Tab key press
if(e.key != 'ArrowRight'){
if (autocomplete[0].textContent != '' && predicted){
var first_character = predicted.charAt(0);
if(e.key == first_character){
var s1 = predicted;
var s2 = s1.substr(1);
predicted = s2;
apibusy = true;
}else{
autocomplete[0].textContent = '';
apibusy= false;
}
}else{
autocomplete[0].textContent = '';
apibusy= false;
}
return;
}else{
if(predicted){
if (apibusy == true){
apibusy= false;
}
if (apibusy== false){
mainInput[0].value = foundName;
autocomplete[0].textContent = '';
}
}else{
return;
}
}
function CallMLDataSetAPI(event) {
//call api and get response
var response = {
"predicted": example[randomobj(example)]
};
if(response.predicted != ''){
predicted = response.predicted;
var new_text = event.target.value + response.predicted;
autocomplete[0].textContent = new_text;
foundName = new_text
}else{
predicted = '';
var new_text1 = event.target.value + predicted;
autocomplete[0].textContent = new_text1;
foundName = new_text1
}
};
});
$('#mainInput').keypress(function(e) {
var sc = 0;
$('#mainInput').each(function () {
this.setAttribute('style', 'height:' + (0) + 'px;overflow-y:hidden;');
this.setAttribute('style', 'height:' + (this.scrollHeight+3) + 'px;overflow-y:hidden;');
sc = this.scrollHeight;
});
$('#autocomplete').each(function () {
if (sc <=400){
this.setAttribute('style', 'height:' + (0) + 'px;overflow-y:hidden;');
this.setAttribute('style', 'height:' + (sc+2) + 'px;overflow-y:hidden;');
}
}).on('input', function () {
this.style.height = 0;
this.style.height = (sc+2) + 'px';
});
});
function scrolltobototm() {
var target = document.getElementById('autocomplete');
var target1 = document.getElementById('mainInput');
setInterval(function(){
target.scrollTop = target1.scrollHeight;
}, 1000);
};
$( "#mainInput" ).keydown(function(e) {
if (e.keyCode === 9) {
e.preventDefault();
presstabkey();
}
});
function presstabkey() {
if(predicted){
if (apibusy == true){
apibusy= false;
}
if (apibusy== false){
mainInput[0].value = foundName;
autocomplete[0].textContent = '';
}
}else{
return;
}
};
});
#autocomplete { opacity: 0.6; background: transparent; position: absolute; box-sizing: border-box; cursor: text; pointer-events: none; color: black; width: 421px;border:none;} .vc_textarea{ padding: 10px; min-height: 100px; resize: none; } #mainInput{ background: transparent; color: black; opacity: 1; width: 400px; } #autocomplete{ opacity: 0.6; background: transparent;padding: 11px 11px 11px 11px; }
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<textarea id="autocomplete" type="text" class="vc_textarea"></textarea>
<textarea id="mainInput" type="text" name="comments" placeholder="Write some text" class="vc_textarea"></textarea>
]1]1
So I was able to find this greate jscript: http://joelb.me/blog/2011/code-snippet-accessing-clipboard-images-with-javascript/
The java is pretty simple (all credit to the author)
// We start by checking if the browser supports the
// Clipboard object. If not, we need to create a
// contenteditable element that catches all pasted data
if (!window.Clipboard) {
var pasteCatcher = document.createElement("div");
// Firefox allows images to be pasted into contenteditable elements
pasteCatcher.setAttribute("contenteditable", "");
// We can hide the element and append it to the body,
pasteCatcher.style.opacity = 0;
document.body.appendChild(pasteCatcher);
// as long as we make sure it is always in focus
pasteCatcher.focus();
document.addEventListener("click", function() { pasteCatcher.focus(); });
}
// Add the paste event listener
window.addEventListener("paste", pasteHandler);
/* Handle paste events */
function pasteHandler(e) {
// We need to check if event.clipboardData is supported (Chrome)
if (e.clipboardData) {
// Get the items from the clipboard
var items = e.clipboardData.items;
if (items) {
// Loop through all items, looking for any kind of image
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
// We need to represent the image as a file,
var blob = items[i].getAsFile();
// and use a URL or webkitURL (whichever is available to the browser)
// to create a temporary URL to the object
var URLObj = window.URL || window.webkitURL;
var source = URLObj.createObjectURL(blob);
// The URL can then be used as the source of an image
createImage(source);
}
}
}
// If we can't handle clipboard data directly (Firefox),
// we need to read what was pasted from the contenteditable element
} else {
// This is a cheap trick to make sure we read the data
// AFTER it has been inserted.
setTimeout(checkInput, 1);
}
}
/* Parse the input in the paste catcher element */
function checkInput() {
// Store the pasted content in a variable
var child = pasteCatcher.childNodes[0];
// Clear the inner html to make sure we're always
// getting the latest inserted content
pasteCatcher.innerHTML = "";
if (child) {
// If the user pastes an image, the src attribute
// will represent the image as a base64 encoded string.
if (child.tagName === "IMG") {
createImage(child.src);
}
}
}
/* Creates a new image from a given source */
function createImage(source) {
var pastedImage = new Image();
pastedImage.onload = function() {
// You now have the image!
}
pastedImage.src = source;
}
I get data:image/png;base64,iVBORw0KG
My question for everybody is how to I paste that back into an textbox multiline or into a text area? Or into anything a user can see?
Thanks!
That example already works, it should show an image but the opacity is set to 0, just change that to 1 and it will show an image.
You can always predefine an image in a location that you want and just set the source of that.
Your user should be able to see that.
I am using jQuery to append text into a textarea
var box = $('<textarea></textarea>')
.attr({id: 'log_viewer',
readonly: '',
cols: 100,
rows: 40})
.appendTo($(outputEntity));
When data is received from a WebSocket, it's appended to the content of the textarea:
ws.onmessage = function(msg) {
var data = decodeURIComponent(msg.data);
data = data.replace('<', '<');
data = data.replace('>', '>');
box.append(data);
}
What I need is for this box to scroll to the bottom automatically, so that the viewport is always centered on the last page of output. Following some other Stack Overflow suggestions on this topic, I tried:
$('#log_viewer').scrollTop = $('#log_viewer')[0].scrollHeight - $('#log_viewer').height();
However, that doesn't work -- at least, not in Chrome or Firefox on Linux, which is all I have access to. The box doesn't scroll.
How do I make this box auto-scroll to the bottom-most page from JS? Is using append() to add output somehow interfering with the underlying dimension detection mechanisms? Is append() even the right way to go there?
Or perhaps I should use not use a textarea at all, but rather a DIV with a scroll bar? Admittedly, I'm rather behind the times on HTML + CSS and don't know exactly how to best accomplish that and still get the monospace-formatted, wrapped output I'm after.
Thanks much for any suggestions!
SOLVED:
http://jsfiddle.net/Ua9qc/
var h1 = $('#log_viewer')[0].scrollHeight,
h2 = $('#log_viewer').height();
$('#log_viewer').scrollTop(h1 - h2);`
I am not sure this will help you. If you wanna scroll certain element at the bottom, you can use this function to scroll by passing div id. Add these function to script
function scrollme(id)
{
var scrollElem = scrollableElement('html', 'body');
var targetOffset = $(id).offset().top;
$(scrollElem).animate({scrollTop: targetOffset-100}, 1000, function() {
});
}
function scrollableElement(els)
{
for (var i = 0, argLength = arguments.length; i <argLength; i++) {
var el = arguments[i],
$scrollElement = $(el);
if ($scrollElement.scrollTop()> 0) {
return el;
} else {
$scrollElement.scrollTop(1);
var isScrollable = $scrollElement.scrollTop()> 0;
$scrollElement.scrollTop(0);
if (isScrollable) {
return el;
}
}
}
return [];
}
I have a HTML5 canvas, which is displaying a number of images and a paragraph of text on the page underneath the canvas. I want the text in the paragraph to be updated to display a different element from a JS array depending on which image the user clicks on.
Currently, I have a 'mousedown' function that looks like this:
_mousedown: function(evt) {
this._setUserPosition(evt);
var obj = this.getIntersection(this.getUserPosition());
if(obj && obj.shape) {
var shape = obj.shape;
this.clickStart = true;
shape._handleEvent('mousedown', evt);
isClickOnImage(evt);
var id = shape.id;
selectTip(id);
}
//init stage drag and drop
if(Kinetic.DD && this.attrs.draggable) {
this._initDrag();
}
}
I tried using the line var id = shape.id to update the ID that's being passed to the function, so that it will get the correct element from my 'tips' array, but for some reason, when I view the page in the browser, and click on an image, the text beneath the canvas is not updated. It seems that this function is not updating the 'id' variable to the ID of whichever image has been clicked.
After looking into this, it seems to me that I will want to use a loop inside the 'mousedown' function, that will take the 'id' of the image on which the click has been detected, and loop through my 'sources' array (which is where all of the images have been loaded from the HTML into the JS), checking at each position whether the image stored at that location has the same ID as that of the image that has been clicked on. If it does, the loop should set the text to the text stored at that position of the array, and if not, it should continue looking through the array until it find it. Would this make sense? I tried adding the following code to the 'mousedown' function, but it doesn't change the text as I expected:
var imageCheckArray = 0;
while(imageCheckArray < sources.length){
if(shape.id == sources[imageCheckArray]){
selectTip(imageCheckArray);
} else {
imageCheckArray++;
}
}
Is there something I'm missing from the loop?
The code for the whole function currently looks like this:
_mousedown: function(evt) {
this._setUserPosition(evt);
var obj = this.getIntersection(this.getUserPosition());
if(obj && obj.shape) {
var shape = obj.shape;
this.clickStart = true;
shape._handleEvent('mousedown', evt);
isClickOnImage(evt);
/*This line needs to get the element of the sources array that has been selected,
and then select the element at the same position from the tips array.*/
//var id = null;
var imageCheckArray = 0;
while(imageCheckArray < sources.length){
if(shape.id == sources[imageCheckArray]){
selectTip(imageCheckArray);
} else {
imageCheckArray++;
}
}
//var id =
//selectTip(id);
}
//init stage drag and drop
if(Kinetic.DD && this.attrs.draggable) {
this._initDrag();
}
}
Edit 11/01/2013 # 16:10
The code for selectTip is:
function selectTip(id){
$("#tipsParagraph").text(tips[id]);
}
and I've put a jsFiddle up here: http://jsfiddle.net/cd8G7/ although the 'result' panel is not showing what I actually see when I view the page in my browser- I get the canvas with all of the images displayed, and the paragraph underneath the canvas shows the text from the first element of my 'tips' array.
Edit 23/01/2013 # 13:50
Here's my isClickOnImage function:
function isClickOnImage(event){
var clickX = event.clientX;
var clickY = event.clientY;
//var imageCheckIteration = 0;
while(imageCheckIteration < sources.length){
if((clickX > sources[imageCheckIteration].x && clickX < sources[imageCheckIteration].x + imageWidth) &&
(clickY > sources[imageCheckIteration].y && clickY < sources[imageCheckIteration].y + imageHeight)){
/*This is where I need to print the variable that holds the text I want to display, but I need to display its contents
outside the canvas, in the <p></p> tags below. */
console.log("Click on image detected");
document.getElementById("tipsParagraph").innerHTML = sources[imageCheckIteration].data-tip /*tips[imageCheckIteration]*/;
} else {
document.getElementById("tipsParagraph").innerHTML = "";
}
}
}
What I intended that this function do is, capture the X & Y coordinates of any click on the canvas, and store them in the variables "clickX" and "clickY". Then, I have a variable called "imageCheckIteration" that has been initialised to 0, and while this variable is less than the length of my "sources" array (which is the array where all of the images have been stored), the function should check whether the click was on an area of the canvas that is covered by one of the images in the array.
If it was, then a console log should display the message "click on image detected", and the line
document.getElementById("tipsParagraph").innerHTML = sources[imageCheckIteration].data-tip;
should set the value of the "tipsParagraph" to be the value of the 'data-tip' attribute of whichever image is at the 'imageCheckIteration' position of the 'sources' array. If the click was detected on an area of the canvas that does not have an image displayed, then the value of the "tipsParagraph" should be set to hold nothing.
However, for some reason, when I view the page in the browser, the 'tipsParagraph' displays the text "This is where the text will be displayed", which is its default value, so that's fine. But, when I click on an image, or click anywhere else on the canvas, the text displayed in the 'tipsParagraph' is not updated.
I can't figure out why this is- can someone point me in the right direction? Does it mean that my isClickOnImage(event) function is never being called?
I simplified the way you are getting a reference to an image through the canvas. The trick here is to swap the z-index of the canvas and the image container and grab the reference to the image on the mouse up event. I don't know of a clean way to get elements behind a canvas, hence the workaround.
$('canvas').bind('mousedown', function(e) {
$('section').css('z-index', 4);
});
$('img').bind('mouseup', function(e) {
$('#tipsParagraph').text($(this).attr('id') + ":" + $(this).attr('alt'));
$('section').css('z-index', 2);
});
The second portion here is grabbing some attributes from the image itself and updating the text inside your div.
You can see more of the solution here.
I'm using Adobe Pro to edit PDFs in which a lot of Additions have been entered in the Form of FreeText Annotations.
Now i want to write a Script to Change the Textcolor in these to Black. It already works for Lines/Circles/... but not for the actual Text.
Here is what i have so far:
/* Bla */
var myDoc = event.target.doc;
if(!myDoc)
console.println("Failed to access document");
else
console.println("Opened Document");
//Color all Comments in Black
function colorComments(myDoc)
{
//Get a list of Comments
var commentList = myDoc.getAnnots();
if(commentList == null)
{
console.println("Failed to get Comments");
}
else
{
console.println("Found " + commentList.length + " Comments, Iterating through comments");
}
//Iterate through the comment List and change the Colors
for each(comment in commentList)
{
if(comment == null)
{
console.println("Found undefined annot!");
}
switch(comment.type)
{
case "FreeText":
{
//change stroke color
comment.strokeColor = color.black;
var contents = comment.richContents;
//Go through all spans and change the text color in each one
for each(s in contents)
{
console.println(s.text);
s.textColor = color.black;
}
break;
}
}
}
}
colorComments(myDoc);
Which prints the Contents of the Text in the Console, but the Color doesn't change at all.
It seems the "Span" object gets copied instead of referenced somewhere in my code.
Creating a array to hold the changed Spans and then assigning the array to comment.richContents seems to work fine.
case "FreeText":
{
var spans = new Array;
for each(span in comment.richContents)
{
span.textColor = color.red;
spans[spans.length] = span;
}
comment.richContents = spans;
break;
}
That works fine. (iterating over comments.richContents directly and changing the for each loop to a for loop didn't change the result though.
The answer to WHY it didn't work probably lies somewhere in the specifics of JS.