How to handle URL relative/absolute "glitch" in contenteditable when copy and pasting - anchor

In contenteditable regions, if you paste an element with a URL attribute, in some browsers it converts the URL from relative to absolute.
I've read through some bug reports that claim it's "fixed" in the latest release, but it's not.
I threw together this fiddle to demonstrate: Hurray for Demos!
It's there, it's ugly, and I'm wondering what is the best way to fix it.
The 1st idea that comes to mind is onpaste, find all anchors in the current node and parse it with regex. Not ideal I suppose, but it might be effective.
???
???
I really wish they'd just leave things alone and not create so many browser related issues with contenteditable, but I guess that would make it too easy.
Any thoughts on the best way to address this?

CKEditor, before letting browser break the data, copies all src, name and href attributes to data-cke-saved-src|href attributes. Unfortunately, since data is a string, it has to be done by regexp. You can find the code here: /core/htmldataprocessor.js#L772-L783.
var protectElementRegex = /<(a|area|img|input|source)\b([^>]*)>/gi,
// Be greedy while looking for protected attributes. This will let us avoid an unfortunate
// situation when "nested attributes", which may appear valid, are also protected.
// I.e. if we consider the following HTML:
//
// <img data-x="<a href="X"" />
//
// then the "non-greedy match" returns:
//
// 'href' => '"X"' // It's wrong! Href is not an attribute of <img>.
//
// while greedy match returns:
//
// 'data-x' => '<a href="X"'
//
// which, can be easily filtered out (#11508).
protectAttributeRegex = /([\w-]+)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi,
protectAttributeNameRegex = /^(href|src|name)$/i;
function protectAttributes( html ) {
return html.replace( protectElementRegex, function( element, tag, attributes ) {
return '<' + tag + attributes.replace( protectAttributeRegex, function( fullAttr, attrName ) {
// Avoid corrupting the inline event attributes (#7243).
// We should not rewrite the existed protected attributes, e.g. clipboard content from editor. (#5218)
if ( protectAttributeNameRegex.test( attrName ) && attributes.indexOf( 'data-cke-saved-' + attrName ) == -1 )
return ' data-cke-saved-' + fullAttr + ' data-cke-' + CKEDITOR.rnd + '-' + fullAttr;
return fullAttr;
} ) + '>';
} );
}
Then, while processing HTML taken from editable element, data-cke-saved-* attributes override the original ones.

This looks like a browser bug that's not specific to contenteditable: https://bugzilla.mozilla.org/show_bug.cgi?id=805359
That issue was opened 10 years ago and last updated 6 years ago. Yet it's still open.
You can see the bug here on StackOverflow. Inspecting any SO link shows that the href value is a relative URL. Copying it and pasting it as HTML has the relative link rewritten into an absolute URL.
Example:

Related

Copying, Pasting, Making - links for and in TinyMCE

I'm using TinyMCE in a Filemaker solution for Note taking on a Mac.
I have found that I can create links between notes, via a simple URL, using fmp://the-open-file...etc..etc.  It's very cool and useful, but I found that TinyMCE is fussy about its links.
I can make the relevant text for the link and paste it into Tiny, then use it to manually create a link, as per normal.  However, it would be much better to be able to make the link, with a label/title (very important in terms of practicality, using the Note's name) and simply paste it into Tiny and it's done.
So to this end, I got some help with a custom function for Tiny which makes the link according Tiny's requirements and it works!  It was also specifically to enable Labels/Titles to be a part of the links created in Filemaker.  However, I've found that there's something that Tiny doesn't like, with the consequence that I cannot copy these links from Tiny in one Note and paste it into Tiny in another Note.  When copied, the URL is lost.  It is also no longer coloured with the custom css I've added for Link colour... but... the hover state is still applied. Hmmm.
Can anyone suggest where this may be going wrong and how I can get around the problem?
This is the custom function in Tiny, which is triggered from Filemaker.
<script>
function copyLink(url,text) {
var a = document.createElement('a');
a.setAttribute("href",url);
a.appendChild(document.createTextNode(text));
document.body.appendChild(a);
var range = document.createRange();
range.selectNode(a);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.execCommand('copy');
document.body.removeChild(a);
}
</script>
These are how the link is made in a Filemaker calculation, with
1/ link to file
2/ name of a script which is triggered by the link
3/ the NoteName and IDNote as parameters for that script
Perform Script   -  Javascript in Web-Viewer
Object Name: "" ;
Function Name: "copyLink";
Script Parameters
"fmp://$/" & Get ( FileName )
& ".fmp12?script="
& GetAsURLEncoded ( "LinkToGSNote" )
& "&param="
& GetAsURLEncoded
( JSONSetElement ( "" ; [ "noteName" ; Notes::NoteName ; JSONString ] ;
[ "IDNote" ; Notes::ID_Notes ; JSONString ] )
GetAsText ( Notes::NoteName )

Add parameters to the URL (redirect) via a Greasemonkey/Tampermonkey/Userscript

I'd like to write a Greasemonkey/userscript that automatically adds .compact to URLs starting with https://pay.reddit.com/ so It automatically redirects me to the mobile version.
I've been looking at similar userscripts, particularly this one: https://userscripts.org/scripts/review/112568 trying to figure out how to edit the replacement pattern, but I lack skills in this domain.
How do I write a Greasemonkey script that redirects me from https://pay.reddit.com/* to https://pay.reddit.com/*.compact ?
Thanks
The script should do these things:
Detect if the current URL is already to the compact site.
Load the compact version of the page if necessary.
Beware of "anchor" URLS (they end with "fragments" or "hashes" (#...) ) and account for them.
Keep the unwanted pages out of the browser history so that the back button works well. Only .compact URL's will be remembered.
By running at document-start, the script can give better performance in this case.
To that end, this script works:
// ==UserScript==
// #name _Reddit, ensure compact site is used
// #match *://*.reddit.com/*
// #run-at document-start
// #grant none
// ==/UserScript==
var oldUrlPath = window.location.pathname;
/*--- Test that ".compact" is at end of URL, excepting any "hashes"
or searches.
*/
if ( ! /\.compact$/.test (oldUrlPath) ) {
var newURL = window.location.protocol + "//"
+ window.location.host
+ oldUrlPath + ".compact"
+ window.location.search
+ window.location.hash
;
/*-- replace() puts the good page in the history instead of the
bad page.
*/
window.location.replace (newURL);
}
The example script you showed is using a regex to manipulate the window's location:
replace(/^https?:\/\/(www\.)?twitter.com/, 'https://mobile.twitter.com');
Unsurprisingly, this replaces https://www.twitter.com and http://twitter.com etc. with https://mobile.twitter.com.
Your situation is slightly different, because you want to append a string to your url if it matches some regex. Try:
var url = window.location.href;
var redditPattern = /^https:\/\/pay.reddit.com\/.*/;
// Edit: To prevent multiple redirects:
var compactPattern = /\.compact/;
if (redditPattern.test(url)
&& !compactPattern.test(url)) {
window.location.href = url + '.compact';
}
See: http://jsfiddle.net/RichardTowers/4VjdZ/3 for test case.

jQuery Mobile Filtered List - only match beginning of string

Im using the jQuery mobile search filter list:
http://jquerymobile.com/test/docs/lists/lists-performance.html
Im having somer performance issues, my list is a little slow to filter on some phones. To try and aid performance I want to change the search so only items starting with the search text are returned.
So 'aris' currently finds the result 'paris' but I want this changed. I can see its possible from the documentation below but I dont know how to implement the code.
http://jquerymobile.com/test/docs/lists/docs-lists.html
$("document").ready( function (){
$(".ui-listview").listview('option', 'filterCallback', yourFilterFunction)
});
This seems to demonstrate how you write and call your own function, but ive no idea how to write it! Thanks
http://blog.safaribooksonline.com/2012/02/14/jquery-mobile-tip-write-your-own-list-view-filter-function/
UPDATE - Ive tried the following in a seperate js file:
$("document").ready( function (){
function beginsWith( text, pattern) {
text= text.toLowerCase();
pattern = pattern.toLowerCase();
return pattern == text.substr( 0, pattern.length );
}
$(".ui-listview").listview('option', 'filterCallback', beginsWith)
});
might look something like this:
function beginsWith( text, pattern) {
text= text.toLowerCase();
pattern = pattern.toLowerCase();
return pattern == text.substr( 0, pattern.length );
}
Basically you compare from 0 to "length" of what you're matching to the source. So if you pass in "test","tester" it will see you're passing in a string of length 4 and then substr "tester" from 0,4, which gives you "test". Then "test" is equal to "test"... so return true. Lowercase them to make it case insensitive.
Another trick to improve filter performance, only filter once they've entered more than 1 character.
edit it appears jQueryMobile's filter function expects that "true" means it was not found... so it needs to be backwards. return pattern != text.substr( 0, pattern.length );
This worked for me. I am using regular expression here so sort of different way to achieve the same thing.
But the reason why my code didn't work initially was that the list item had a lot of spaces at the beginning and at the end (found that it got added on it's own while debugging).
So I do a trim on the text before doing the match. I have a feeling Jonathan Rowny's implementation will also work if we do text.trim() before matching.
$(".ui-listview").listview('option', 'filterCallback', function (text, searchValue) {
var matcher = new RegExp("^" + searchValue, "i");
return !matcher.test(text.trim());
});

Get fragment (value after hash '#') from a URL [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
How can i select the fragment after the '#' symbol in my URL using PHP?
The result that i want is "photo45".
This is an example URL:
http://example.com/site/gallery/1#photo45
If you want to get the value after the hash mark or anchor as shown in a user's browser: This isn't possible with "standard" HTTP as this value is never sent to the server (hence it won't be available in $_SERVER["REQUEST_URI"] or similar predefined variables). You would need some sort of JavaScript magic on the client side, e.g. to include this value as a POST parameter.
If it's only about parsing a known URL from whatever source, the answer by mck89 is perfectly fine though.
That part is called "fragment" and you can get it in this way:
$url=parse_url("http://example.com/site/gallery/1#photo45 ");
echo $url["fragment"]; //This variable contains the fragment
A) already have url with #hash in PHP? Easy! Just parse it out !
if( strpos( $url, "#" ) === false ) echo "NO HASH !";
else echo "HASH IS: #".explode( "#", $url )[1]; // arrays are indexed from 0
Or in "old" PHP you must pre-store the exploded to access the array:
$exploded_url = explode( "#", $url ); $exploded_url[1];
B) You want to get a #hash by sending a form to PHP?     => Use some JavaScript MAGIC! (To pre-process the form)
var forms = document.getElementsByTagName('form'); //get all forms on the site
for (var i = 0; i < forms.length; i++) { //to each form...
forms[i].addEventListener( // add a "listener"
'submit', // for an on-submit "event"
function () { //add a submit pre-processing function:
var input_name = "fragment"; // name form will use to send the fragment
// Try search whether we already done this or not
// in current form, find every <input ... name="fragment" ...>
var hiddens = form.querySelectorAll('[name="' + input_name + '"]');
if (hiddens.length < 1) { // if not there yet
//create an extra input element
var hidden = document.createElement("input");
//set it to hidden so it doesn't break view
hidden.setAttribute('type', 'hidden');
//set a name to get by it in PHP
hidden.setAttribute('name', input_name);
this.appendChild(hidden); //append it to the current form
} else {
var hidden = hiddens[0]; // use an existing one if already there
}
//set a value of #HASH - EVERY TIME, so we get the MOST RECENT #hash :)
hidden.setAttribute('value', window.location.hash);
}
);
}
Depending on your form's method attribute you get this hash in PHP by:
$_GET['fragment'] or $_POST['fragment']
Possible returns: 1. ""[empty string] (no hash) 2. whole hash INCLUDING the #[hash] sign (because we've used the window.location.hash in JavaScript which just works that way :) )
C) You want to get the #hash in PHP JUST from requested URL?
                                    YOU CAN'T !
...(not while considering regular HTTP requests)...
...Hope this helped :)
I've been searching for a workaround for this for a bit - and the only thing I have found is to use URL rewrites to read the "anchor". I found in the apache docs here http://httpd.apache.org/docs/2.2/rewrite/advanced.html the following...
By default, redirecting to an HTML anchor doesn't work, because mod_rewrite escapes the # character, turning it into %23.
This, in turn, breaks the redirection.
Solution: Use the [NE] flag on the RewriteRule. NE stands for No
Escape.
Discussion: This technique will of course also work with other special
characters that mod_rewrite, by default, URL-encodes.
It may have other caveats and what not ... but I think that at least doing something with the # on the server is possible.
You can't get the text after the hash mark. It is not sent to the server in a request.
I found this trick if you insist want the value with PHP.
split the anchor (#) value and get it with JavaScript, then store as cookie, after that get the cookie value with PHP
If you are wanting to dynamically grab the hash from URL, this should work:
https://stackoverflow.com/a/57368072/2062851
<script>
var hash = window.location.hash, //get the hash from url
cleanhash = hash.replace("#", ""); //remove the #
//alert(cleanhash);
</script>
<?php
$hash = "<script>document.writeln(cleanhash);</script>";
echo $hash;
?>
You can do it by a combination of javascript and php:
<div id="cont"></div>
And by the other side;
<script>
var h = window.location.hash;
var h1 = (win.substr(1));//string with no #
var q1 = '<input type="text" id="hash" name="hash" value="'+h1+'">';
setInterval(function(){
if(win1!="")
{
document.querySelector('#cont').innerHTML = q1;
} else alert("Something went wrong")
},1000);
</script>
Then, on form submit you can retrieve the value via $_POST['hash'] (set the form)
You need to parse the url first, so it goes like this:
$url = "https://www.example.com/profile#picture";
$fragment = parse_url($url,PHP_URL_FRAGMENT); //this variable holds the value - 'picture'
If you need to parse the actual url of the current browser, you need to request to call the server.
$url = $_SERVER["REQUEST_URI"];
$fragment = parse_url($url,PHP_URL_FRAGMENT); //this variable holds the value - 'picture'
Getting the data after the hashmark in a query string is simple. Here is an example used for when a client accesses a glossary of terms from a book. It takes the name anchor delivered (#tesla), and delivers the client to that term and highlights the term and its description in blue so its easy to see.
setup your strings with a div id, so the name anchor goes where its supposed to and the JavaScript can change the text colors
<div id="tesla">Tesla</div>
<div id="tesla1">An energy company</div>
Use JavaScript to do the heavy work, on the server side, inserted in your PHP page, or wherever..
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
I am launching the Java function automatically when the page is loaded.
<script>
$( document ).ready(function() {
get the anchor (#tesla) from the URL received by the server
var myhash1 = $(location).attr('hash'); //myhash1 == #tesla
trim the hash sign off of it
myhash1 = myhash1.substr(1) //myhash1 == tesla
I need to highlight the term and the description so I create a new var
var myhash2 = '1';
myhash2 = myhash1.concat(myhash2); //myhash2 == tesla1
Now I can manipulate the text color for the term and description
var elem = document.getElementById(myhash1);
elem.style.color = 'blue';
elem = document.getElementById(myhash2);
elem.style.color = 'blue';
});
</script>
This works. client clicks link on client side (example.com#tesla) and goes right to the term. the term and the description are highlighted in blue by JavaScript for quick reading .. all other entries left in black..

How to parse a remote website and create a link on every single word for a dictionary tooltip?

I want to parse a random website, modify the content so that every word is a link (for a dictionary tooltip) and then display the website in an iframe.
I'm not looking for a complete solution, but for a hint or a possible strategy. The linking is my problem, parsing the website and displaying it in an iframe is quite simple. So basically I have a String with all the html content. I'm not even sure if it's better to do it serverside or after the page is loaded with JS.
I'm working with Ruby on Rails, jQuery, jRails.
Note: The content of the href tag depends on the word.
Clarification:
I tried a regexp and it already kind of works:
#site.gsub!(/[A-Za-z]+(?:['-][A-Za-z]+)?|\\d+(?:[,.]\\d+)?/) {|word| '' + word + ''}
But the problem is to only replace words in the text and leave the HTML as it is. So I guess it is a regex problem...
Thanks for any ideas.
I don't think a regexp is going to work for this - or, at least, it will always be brittle. A better way is to parse the page using Hpricot or Nokogiri, then go through it and modify the nodes that are plain text.
It sounds like you have it mostly planned out already.
Split the content into words and then for each word, create a link, such as whatever
EDIT (based on your comment):
Ahh ... I recommend you search around for screen scraping techniques. Most of them should start with removing anything between < and > characters, and replacing <br> and <p> with newlines.
I would use Nokogiri to remove the HTML structure before you use the regex.
no_html = Nokogiri::HTML(html_as_string).text
Simple. Hash the HTML, run your regex, then unhash the HTML.
<?php
class ht
{
static $hashes = array();
# hashes everything that matches $pattern and saves matches for later unhashing
function hash($text, $pattern) {
return preg_replace_callback($pattern, array(self,'push'), $text);
}
# hashes all html tags and saves them
function hash_html($html) {
return self::hash($html, '`<[^>]+>`');
}
# hashes and saves $value, returns key
function push($value) {
if(is_array($value)) $value = $value[0];
static $i = 0;
$key = "\x05".++$i."\x06";
self::$hashes[$key] = $value;
return $key;
}
# unhashes all saved values found in $text
function unhash($text) {
return str_replace(array_keys(self::$hashes), self::$hashes, $text);
}
function get($key) {
return self::$hashes[$key];
}
function clear() {
self::$hashes = array();
}
}
?>
Example usage:
ht::hash_html($your_html);
// your word->href converter here
ht::unhash($your_formatted_html);
Oh... right, I wrote this in PHP. Guess you'll have to convert it to ruby or js, but the idea is the same.

Resources