Drop "draggable" into Handsontable cell - javascript

I'm tiring to use Handsontable jQuery module with jQuery UI drag&drop functionality, but without success. I've fount some code with insertAtCaret function, but I have troubles using it with Handsontable.
I want to be able, to drag element from outside of Handsontable table, and drop it into cell. I know, that I have to update cell somehow...
Please help...
CODE:
$.fn.insertAtCaret = function (myValue) {
return this.each(function(){
//IE support
if (document.selection) {
this.focus();
sel = document.selection.createRange();
sel.text = myValue;
this.focus();
}
//MOZILLA / NETSCAPE support
else if (this.selectionStart || this.selectionStart == '0') {
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
var scrollTop = this.scrollTop;
this.value = this.value.substring(0, startPos)+ myValue+ this.value.substring(endPos,this.value.length);
this.focus();
this.selectionStart = startPos + myValue.length;
this.selectionEnd = startPos + myValue.length;
this.scrollTop = scrollTop;
} else {
this.value += myValue;
this.focus();
}
});
};
My JSFiddle with source

The insertAtCaret code is not really what you need for this situation. You've already got a good handle on the required transaction - you need to use the ui.draggable element in the drop event to manually add some text to the correct cell. You just need a few final steps.
Now, one key piece of information is that Handsontable wants you to set cell data by coordinate, which means you need to know the column and row of the thing you want to set, not just have a javascript reference to the actual dom element. For that, we can use jQuery's index method.
Find out what cell the draggable has been dropped on.
Determine what that cell's column and row are.
Use Handsontable's setDataAtCell method to change the data.
Live Demo
$("#excel_table").droppable({
...
drop: function(event, ui) {
// Get a reference to the handsontable instance
var ht = $('#excel_table').handsontable('getInstance');
// Hide the helper, so that we can use .elementFromPoint
// to grab the item beneath the cursor by coordinate
ui.helper.hide();
var $destination = $(document.elementFromPoint(event.clientX, event.clientY));
// Grab the parent tr, then the parent tbody so that we
// can use their index to find the row and column of the
// destination object
var $tr = $destination.closest('tr');
var $tbody = $tr.closest('tbody');
var col = $tr.children().index($destination);
var row = $tbody.children().index($tr);
// Use the setDataAtCell method, which takes a row and
// col number, to adjust the data
ht.setDataAtCell(row, col, ui.draggable.text());
},
...
});

Related

How to check what are adjacent characters?

I'm creating a markdown editor and I need to check if neighbor characters are specific characters, then remove them, else append them.
For e.g I want to check selected-text, tow neighbor characters are **, then remove them, else append them around selected text.
I can get selected text using this approach:
function getSelection(elem) {
var selectedText;
if (document.selection != undefined) { // IE
elem.focus();
var sel = document.selection.createRange();
selectedText = sel.text;
} else if (elem.selectionStart != undefined) { // Firefox
var startPos = elem.selectionStart;
var endPos = elem.selectionEnd;
selectedText = elem.value.substring(startPos, endPos)
}
return selectedText;
}
$(document).on('mousedown', 'button', function(e) {
var selection = getSelection( $('#txtarea').get(0) );
alert(selection);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="txtarea">this is a test</textarea>
<button>Bold (toggle)</button>
Now I need when user clicks on that button, it checks if selected text is between ** like this **selectedtext**, then remove them like this selected text else append them like this **selectedtext**. How can I do that?
Before anything I would like to refer to all the markdown editors out there: https://www.google.de/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=javascript%20markdown%20library
So: do not try to reinvent the the wheel, and so on.
But for the sake of learning, my approach would look like this:
function toggleMarker(marker, el) {
var markerLength = marker.length;
var startPos, endPos, selection, range;
if (document.selection != undefined) { // IE
el.focus();
range = document.selection.createRange();
selection = range.text;
} else if (el.selectionStart != undefined) { // Firefox
startPos = el.selectionStart;
endPos = el.selectionEnd;
selection = el.value.substring(startPos, endPos);
}
if (!selection.length){
return;
}
if (el.value.substring(startPos-markerLength,startPos) === marker
&& el.value.substring(endPos,endPos+markerLength) === marker
){
el.value = el.value.substring(0,startPos-markerLength) +
selection +
el.value.substring(endPos+markerLength);
}
else{
el.value = el.value.substring(0,startPos) + marker +
selection + marker + el.value.substring(endPos);
}
}
$(document).on('mousedown', 'button', function(e) {
toggleMarker( $(this).data('marker'), $('#txtarea').get(0) ).text;
});
See it in action: https://jsfiddle.net/t4ro53v8/4/
The solution takes a very generic approach: the marker to toggle is set as a custom data attribute to make it easy to reuse the code.
The functionality is only implemented for the non-IE case. You will have to check, how to determine startPos and endPos for a range in IE.
In all other browsers:
the selection is identified
nothing is done if nothing is selected
sourroundings of the selection are checked against the given marker
if both markers are present, they get deleted
otherwise the markers are inserted
As a proof of concept this example works like a charm.
But there are some shortcomings:
How to distinguish between bold text(**) and italics(*)?
How to handle markers that just appear just on one side of the selection
What to do, if a marker is selected?
But that is for you to solve now ...
You could use regex to find the occurance of a ** ** pattern.This regex will help you find the pattern similar to what you have.
[*][*][a-z]*[*][*] .
Using the exec() method, will help you extract that particular text.
Check the length of this using .length, if it is 4, then there is nothing in between, and you can replace it with the new text surrounded by **,
"**"+ newtext+"**"
For removing the **, you can use the replace() method, where you replace ** with whitespace or so.

Get HTML of selection in a specific div

I have found a code snippet (can't remember where), and it's working fine - almost :-)
The problem is, that it copies the selection no matter where the selection is made on the entire website, and it must only copy the selection if it is in a specific div - but how is that done?
function getHTMLOfSelection () {
var range;
if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
return range.htmlText;
}
else if (window.getSelection) {
var selection = window.getSelection();
if (selection.rangeCount > 0) {
range = selection.getRangeAt(0);
var clonedSelection = range.cloneContents();
var div = document.createElement('div');
div.appendChild(clonedSelection);
return div.innerHTML;
} else {
return '';
}
} else {
return '';
}
}
$(document).ready(function() {
$("#test").click(function() {
var kopitekst = document.getElementById("replytekst");
var kopitjek=getHTMLOfSelection(kopitekst);
if (kopitjek=='')
{
alert("Please select some content");
}
else
{
alert(kopitjek);
}
});
});
I have made a Jsfiddle
This is my first post here. Hopefully I done it right :-)
That's because it checks the entire document with:
if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
return range.htmlText;
}
Not a specific section. If you want to check specific sections for selected text, you need to identify that you are searching for them in the search selection, something that nails your range down to a particular div:
range = $('#replytekst');
Specify a particular DOM element instead of using document object.
var oDiv = document.getElementById( 'selDiv' );
then use
if ( oDiv.selection && oDiv.selection.createRange ) {
range = oDiv.selection.createRange();
return range.htmlText;
}
You need to check if the section contains the selection. This is separate from getting the selection. There is a method for doing this in this answer: How to know if selected text is inside a specific div
I've updated your fiddle
Basically you need to check the id of the parent/ascendant of the selected text node.
selection.baseNode.parentElement.id or selection.baseNode.parentElement.parentElement.id will give you that.
Edit: I've thought of another, somewhat hack-y, way of doing it.
If
kopitekst.innerHTML.indexOf(kopitjek) !== -1
gives true, you've selected the right text.
DEMO1
DEMO2
(these work in Chrome and Firefox, but you might want to restructure the getHTMLOfSelection function a little)
If it possible for you I recommend to use rangy framework. Then your code might look like this:
// get the selection
var sel = rangy.getSelection();
var ranges = sel.getAllRanges();
if (!sel.toString() || !sel.toString().length)
return;
// create range for element, where selection is allowed
var cutRange = rangy.createRange();
cutRange.selectNode(document.getElementById("replytekst"));
// make an array of intersections of current selection ranges and the cutRange
var goodRanges = [];
$.each(ranges, function(j, tr) {
var rr = cutRange.intersection(tr);
if (rr)
goodRanges.push(rr);
});
sel.setRanges(goodRanges);
// do what you want with corrected selection
alert(sel.toString());
// release
sel.detach();
In this code if text was selected in your specific div then it will be kept, if there was selection where other elements take part too, these selection ranges will be cut off.

Copying Dynamic Table Column with rows of Hidden style properties to textarea jQuery

I'm pretty new to jQuery and I'm having a little trouble accomplishing a specific function that I want for my table.
I have a db list that gets dynamically sorted and I want to be able to create a textarea that includes the text from a specific column on the click of the column header. I have some of the functionality from the code that I used from this http://jsfiddle.net/4BwGG/3/ but here are some things I just can't figure out:
I have some of the rows in my table hidden using style="display: none" property within the <tr> tag and when the script parses everything, the information from those hidden rows get included too. How do I do a check so that only the displayed rows are copied to the text area?
Here is what one row entry looks like:
<tr filtermatch="false" style="display: none;">
<td>< a href="http://example.edu">Tommy Trojan< /a>< /td>
< td>123-555-1231< /td>
< td>Statue Man< /td>
< td>[LTS1] [LTS2] [PM] [PM2] [TA1] [TA2] < /td>
< td>tommy#example.edu< /td>
< /tr>`
Here is the Function:
function SelectColumn(index, tableId) {
var columnText = 'You selected:\n\n';
var columnSelector = '#' + tableId + ' tbody > tr > td:nth-child(' + (index + 1) + ')';
var cells = $(columnSelector);
// clear existing selections
if (window.getSelection) { // all browsers, except IE before version 9
window.getSelection().removeAllRanges();
}
if (document.createRange) {
cells.each(function(i, cell) {
var rangeObj = document.createRange();
rangeObj.selectNodeContents(cell);
window.getSelection().addRange(rangeObj);
columnText = columnText + '\n' + rangeObj.toString();
});
}
else { // Internet Explorer before version 9
cells.each(function(i, cell) {
var rangeObj = document.body.createTextRange();
rangeObj.moveToElementText(cell);
rangeObj.select();
columnText = columnText + '\n' + rangeObj.toString();
});
}
alert(columnText);
}
Try wrapping the code in a conditional statement that checks the visibility of the tr.
For example:
if (document.createRange) {
cells.each(function(i, cell) {
if ($(cell).closest('tr').is(':visible')) {
var rangeObj = document.createRange();
rangeObj.selectNodeContents(cell);
window.getSelection().addRange(rangeObj);
columnText = columnText + '\n' + rangeObj.toString();
}
});
}
Of course, you'd want to do the same thing in the else block as well. But for the record, that jsFiddle did not work for me in IE7 (it throws an error about unsupported property or method).
I know you didn't ask, but unless you need the column to actually be selected, I would refactor the code. If you want the column to appear selected, I'd probably add a little CSS.
Someone else could probably improve the code even more. But here is my suggestion. I've added comments to explain what I did and why.
function SelectColumn(index, tableId) {
// cache the table selector in a local variable
// because we are going to use it more than once
var columnText = 'You selected:\n\n',
table = $('#' + tableId),
cells = table.find('td:nth-child(' + (index + 1) + ')');
// reset the background color of all cells
table.find('td').css('background-color', '#fff');
cells.each(function(i, cell) {
// turn cell into a jQuery object and cache it
// because we are going to use it more than once
cell = $(cell);
if (cell.closest('tr').is(':visible')) {
// get the cell text and trim it
// because different browsers treat newlines differently
columnText += $.trim(cell.text()) + '\n';
// set a background color on the selected cells
cell.css('background-color', '#ccc');
}
});
alert(columnText);
}
If you are using jquery it will make things very easy.
To select only visible elements you can use :visible in jquery.
$(document).ready(function(){
var textAreaContent=[];
$('tr:visible').each(function(){
var content=$('<div />').append($(this).clone()).html();
textAreaContent.push(content);
});
$('.textarea').val(textAreaContent.join(''));
});
check on jsfiddle http://jsfiddle.net/sudhanshu414/9YeLm/
Other option of selecting is using filter. This can also be useful if you want to filter on some other condition.
$(document).ready(function(){
var textAreaContent=[];
$('tr').filter(function(){ return $(this).css('display')!='none';}).each(function(){
var content=$('<div />').append($(this).clone()).html();
textAreaContent.push(content);
});
$('.textarea').val(textAreaContent.join(''));
});
Jsfiddle : http://jsfiddle.net/sudhanshu414/3GfqN/

Set anchor name with execCommand

I know how to set an <a /> tag with the href attribute in a contenteditable like this:
execCommand("CreateLink", false, "#jumpmark");
which will result in
selection
However I cannot figure out how to set an anchor name instead of the href.
This is my desired result:
<a name="jumpmark">selection</a>
Can anyone help me?
Side notes: I am using jQuery and Rangy as libraries, however I would prefer a solution that works directly with execCommand.
Update: Here's a jsfiddle: http://jsfiddle.net/fjYHr/ Select some text and click the button. All I want is that with the button click a link is inserted with a name attribute set instead of the href.
You could use something like the following, which is adapted from the pasteHtmlAtCaret() function from this answer of mine:
Demo: http://jsfiddle.net/F8Zny/
Code:
function surroundSelectedText(element) {
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
element.appendChild( document.createTextNode(range.toString()) );
range.deleteContents();
range.insertNode(element);
// Preserve the selection
range = range.cloneRange();
range.setStartAfter(element);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
var selRange = document.selection.createRange();
element.appendChild( document.createTextNode(selRange.text) );
selRange.pasteHTML(element.outerHTML);
}
}
If you must use document.execCommand() then you could use the InsertHTML command in non-IE browsers. However, IE does not support it.
document.execCommand("InsertHTML", false, '<a name="jumpmark">selection</a>');
I see you're using Rangy, but I don't how to use it at all. Before I realized what Rangy was, I looked up how to get the current selection. I found a function that gets it and replaces it with a passed in value. I ended up modfiying it, but here it is:
http://jsfiddle.net/fjYHr/1/
$(document).ready(function () {
$("#setlink").click(function () {
replaceSelectedText("jumplink");
});
});
function replaceSelectedText(nameValue) {
var sel, sel2, range;
if (window.getSelection) {
sel = window.getSelection();
sel2 = ""+sel; // Copy selection value
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
var newA = document.createElement("a");
newA.name = nameValue;
newA.innerHTML = sel2;
range.insertNode(newA);
}
} else if (document.selection && document.selection.createRange) {
// Not sure what to do here
range = document.selection.createRange();
var newA = "<a name='" + nameValue.replace(/'/g, "") + "'>" + range.text + "</a>";
range.text = newA;
}
}
Notice how I store the original current selection, then replace it with an <a> element that gets its name set with the passed-in value.
As for the document.selection part (which seems to be used by IE < 9), I'm not 100% sure that the code I provided will work (actually allow HTML in the selection, and not escaping it). But it's my attempt :)
As you've seen execCommand is rather limited in the attributes you can set, as such you cannot set the name attribute using it - only the href.
As you have jQuery set as a tag, you can use that as an alternative:
var $a = $('<a></a>').attr('name', 'jumpmark').appendTo('body');
Update
I need to work on the current selection. Specifically I don't have a jQuery object that I can append to, meaning I don't have a DOM node that I can work on
In this case use a plugin such as Rangy to get the selection which you can then amend with jQuery as required.

Move cursor at the end of the input

This is my little jquery plugin that replaces each english symbol with other symbols while typing. Everything works fine here except when I type a long word, longer than the input itself, the cursor goes outside the input the the last part of the word isn't visible. Just visit the link bellow and type something (without the space) and you'll get what I mean.
http://jsfiddle.net/beLMf/
Is there any solution to fix this?
You have two problems one is that you add the text to then end of the line all the time that is if it matches the characters you are looking for. You should instead do the insert at the caret(cursor) position:
jQuery.fn.extend({
insertAtCaret: function(myValue){
return this.each(function(i) {
if (document.selection) {
//For browsers like Internet Explorer
this.focus();
sel = document.selection.createRange();
sel.text = myValue;
this.focus();
}
else if (this.selectionStart || this.selectionStart == '0') {
//For browsers like Firefox and Webkit based
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
var scrollTop = this.scrollTop;
this.value = this.value.substring(0, startPos)+myValue+this.value.substring(endPos,this.value.length);
this.focus();
this.selectionStart = startPos + myValue.length;
this.selectionEnd = startPos + myValue.length;
this.scrollTop = scrollTop;
} else {
this.value += myValue;
this.focus();
}
})
}
});
Then change your line:
$this.val( $this.val() + String.fromCharCode( i + 4304 ) );
to
$this.insertAtCaret(String.fromCharCode( i + 4304 ));
$("#switcher").focus();
$(this).focus();
that will solve your issue and also solve the problem of editing any part of the string.
update:
To make the cursor position update we have to switch focus away and then back to the element
I made the changes to jsFiddle there is also a function to capture cursor position that I was thinking of using to solve your problem there. It just outputs to console.log and you can delete that.

Categories