wysiwyg-editor iframe body

Status
Niet open voor verdere reacties.

Lapixx

Gebruiker
Lid geworden
2 mei 2008
Berichten
132
---------- Opgelost ----------

Hoi,

een tijdje geleden ben ik begonnen aan een soort WYSIWYG editor. Na ik een aantal tutorials heb gelezen (die er niet al te veel waren) zag ik dat je zo'n editor kunt maken met een iframe. Nu vond ik dat niet echt een geweldige optie, maar textarea's en DIVs blijken niet overal te kunnen worden bewerkt.

Hoe dan ook, ik heb nu een iframe op de pagina, die ik met het volgende omzet naar de designmode:

Code:
if(window.navigator.appName=="Microsoft Internet Explorer"){
workfield.document.designMode="on";
}
if(window.navigator.appName=="Netscape"){
document.getElementById("workfield").contentDocument.designMode="on"; 
}

Zoals je ziet heeft MSIE een andere opbouw van het iframe... Overigens zie ik nu dat ik met navigator.appName werk, hoe kan ik dit ook alweer beter doen?

Terug naar het probleem: in een onzichtbare textarea laat ik een opgeslagen document mbv PHP inladen. Bij de body onload word deze inhoud naar het iframe gezet (nadat de design mode aanstaat):

Code:
document.getElementById('workfield').contentWindow.document.body.innerHTML=unescape(document.getElementById('savedcontent').value);

Dit werkt allemaal goed in FF en Safari, maar in MSIE word aangegeven dat het object x.contentWindow.document.body niet bestaat ("is leeg of geen object"). Deze body word in FF en Safari blijkbaar vanzelf aangemaakt, maar in MSIE gebeurt dat niet.

Hoe maak ik deze body aan voordat ik de opgeslagen content in het iframe kan plaatsen? En is er misschien een betere manier om een iframe te vervangen?

Alvast bedankt
 
Laatst bewerkt:
Helaas, aan een iframe zit je vast totdat div's in alle grote browsers edit-baar zijn. Maar, ook met de huidige iframes is het nodige mis. FF, IE en elke browser op zich heeft verschillende manieren hoe ze de html in de WYSIWYG editor schrijven. Het is alsof je een html document maakt met Microsoft Word. Een ramp. Daarnaast heeft een iframe zijn eigen selectie. Je kunt dus gerust buiten de WYSISYG editor klikken, je selectie in de editor blijft staan.



Maargoed, je dacht dat dit beter kan:
PHP:
if(window.navigator.appName=="Microsoft Internet Explorer"){
workfield.document.designMode="on";
}
if(window.navigator.appName=="Netscape"){
document.getElementById("workfield").contentDocument.designMode="on"; 
}

Dat kan inderdaad beter. Om te beginnen werkt dit alleen in IE en Netscape, in alle andere browsers gebeurt er niks. Ik zou het herschrijven naar dit:
PHP:
var workfield = document.getElementById('workfield');
workfield.contentWindow.document.designMode="on";
Dit zou in practisch alle browsers moeten werken.



Dit werkt allemaal goed in FF en Safari, maar in MSIE word aangegeven dat het object x.contentWindow.document.body niet bestaat ("is leeg of geen object"). Deze body word in FF en Safari blijkbaar vanzelf aangemaakt, maar in MSIE gebeurt dat niet.

Hoe maak ik deze body aan voordat ik de opgeslagen content in het iframe kan plaatsen?

Hier is geen javascript voor nodig. Je moet simpelweg een html document in het iframe laden. En dan natuurlijk wel eentje met een body. Het simpelste is om een bestand aan te maken "leeg.html" (o.i.d.) en die in het iframe laden. Dat doe je gewoon in de html:
HTML:
<iframe src="leeg.html"></iframe>

En leeg.html kan er dan simpelweg zo uit zien:
HTML:
<html>
<head>
<title></title>
</head>
<body>
</body>
</html>
Als je al zoiets doet, kun je dan de HTML bron laten zien van de pagina met het iframe en de HTML bron van de pagina die je in eht iframe laadt?
 
Laatst bewerkt:
Dat met Netscape klopt inderdaad, ik had dat deel letterlijk uit een tutorial gehaald en het stuk werkte in de 3 browsers perfect.

Een lege pagina in het iframe zetten, had ik niet aan gedacht. Zal ik morgen eens proberen (al neem ik aan dat het werkt als verwacht).

De editors zijn inderdaad een ramp. Elke browser en OS gaat anders om met de designmode. Zo word in Safari onder Mac overal de class Apple-span toegevoegd. Maar zoals je zelf al zei is er geen andere betere methode. Hopen op wat W3C standaarden en browser support dus :)

Bedankt voor je hulp in ieder geval!
 
Hmmm, helaas. MSIE blijft stug de foutmelding weergeven, ook al staat er nu dus inderdaad een lege body in het iframe.

x.contentWindow.document.designMode="on" werkte overigens wel in MSIE, FF, Safari. Bedankt daarvoor ;)

EDIT:

Ik heb een (soort van) oplossing gevonden. Wanneer je de designmode op 'on' zet in MSIE, is de body niet direct aangemaakt (oid). Wanneer je de setTimeout functie gebruikt om de content van het iframe aan te passen, en de interval op 0 ms zet, werkt alles perfect. En aangezien de interval op 0 staat merken andere browsers hier niets van.

Code:
document.getElementById('workfield').contentWindow.document.designMode="on";  
setTimeout("document.getElementById('workfield').contentWindow.document.body.innerHTML=unescape(document.getElementById('savedcontent').value)",0);

Hoe dan ook, ik zie nu dat ik met nog een probleem heb te kampen.
Speciale characters (letters met een accent bijvoorbeeld) worden door PHP niet goed weergegeven, en daarom word de opgeslagen (html-) pagina verstuurd nadat de functie htmlentities() is toegepast.
Wat dit inhoud voor het client-side gedeelte, is dat de HTML code die in de WYSIWYG editor word geladen, niet meer als HTML maar als platte tekst word gezien (htmlentities zet bijvoorbeeld ook alle < om naar &lt;).

Wanneer ik deze tekst in het iframe zet met innerHTML krijg je dus geen opgemaakte tekst te zien, maar de achterliggende HTML code (ook al is deze data unescape()'d). Hoe 'converteer' ik dit weer naar HTML zodat de browser het correct weergeeft?
 
Laatst bewerkt:
Hmm kun je je volledige HTML bron laten zien? Dit klinkt nogal vreemdt namelijk. Ookal is het niet onmogelijk. Het zou kunnen dat in IE eerst je eerste pagina compleet wordt geladen voordat het iframe geladen wordt. In dat geval kun je je code uitvoeren in de onload event van het iframe. Dat is betrouwbaarder dan een timeout.

Over je andere probleem, de javascript functie unescape() is niet het tegenovergestelde van htmlentities() in PHP. unescape() vertaalt een url-encoded string terug naar het orgineel. Dus iets als dit: "Hallo%20wereld" wordt "Hallo wereld". Dat kan trouwens ook. Je kunt in php urlencode() gebruiken in plaats van htmlentities(). Dan kun je in Javascript unescape() gebruiken.
 
Het gebruiken van het onload event van het iframe werkte niet. Ik heb op een aantal fora gelezen dat de timeout (op 0 ms) een oplossing is voor MSIE, ook al heb ik zelf geen idee waarom (aangezien een timeout van 0ms niet echt iets doet), het werkt. Dit is dus gewoon een bug van de browser.

Ik haalde met unescape() en htmlentities() inderdaad wat dingen door elkaar. Overigens is urlencode geen goede optie, omdat dit meer bedoeld is voor URLs (spaties worden bijvoorbeeld plusjes).

htmlentities werkt echter wel goed, PHP kan de tekst &aacute; foutloos schrijven (uiteraard), en mijn XHR krijgt dan de volledige bron van de opgeslagen pagina terug. Nadeel, zoals ik al zei, is dat de < en > symbolen ook worden omgezet naar een entity code. Ik heb nu echter een manier gevonden om dit terug te vertalen naar HTML:

Wanneer het XHR voltooid is gebruik ik de innerHTML functie om de broncode in een (onzichtbare) textarea te zetten. Doordat de character entities hierin worden gedecodeerd naar het goede character, kun je de value van de textarea opvragen en deze met innerHTML in het iframe zetten.

De textarea dient dus als tijdelijke opslagplaats waar de tekens vanzelf worden gedecodeerd waarna de inhoud ervan geschikt is om als HTML in het iframe te plaatsen.

Het klinkt inderdaad een beetje omslachtig, maar er zijn geen standaardfuncties om de character entities om te zetten naar een character in Javascript. Hierbij is het probleem opgelost, tenzij ik iets over het hoofd zie en alles wat efficiënter kan?
 
Ik haalde met unescape() en htmlentities() inderdaad wat dingen door elkaar. Overigens is urlencode geen goede optie, omdat dit meer bedoeld is voor URLs (spaties worden bijvoorbeeld plusjes).

Dat klopt, maar plusjes worden vertaald naar %43 dus er gaat geen data verloren. Anders kun je rawurlencode() gebruiken in php en encodeURIComponent() in javascript, die vertalen spaties naar %20 en werkt verder hetzelfde. En het is misschien oorspronkelijk ontworpen voor URL's maar dat is geen reden om het niet te gebruiken. Daarnaast wordt het ook gebruikt voor het versturen van POST gegevens (wat erg lang kan zijn) en veel developers gebruiken het om hun cookies veilig op te kunnen slaan. Het is dus zeker bruikbaar voor wat jij wilt. Het decoden is veel simpeler dan een textarea gebruiken, maar nog belangrijker, efficienter. Ik zou dus zeker gaan voor rawurlencode() in PHP en decodeURIComponent() in javascript.

Als het gaat om grote van de data is rawurlencode een betere optie dan htmlentities aangezien %C3 minder ruimte in beslag neemt dan &Atilde;

Er is in PHP trouwens wel een manier om html entities terug te veranderen, maar niet in javascript inderdaad.
http://nl3.php.net/html_entitiy_decode

/edit:
encodeURIComponent() is volledig unicode veilig net als rawurlencode()
 
Laatst bewerkt:
Oké, dan zal ik deze functies eens proberen.

EDIT: Hmmm, het PHP bestand vertaald alles inderdaad goed, maar het Javascript gedeelte loopt vast. Geen errors, niks... Even kijken of het misschien aan iets anders ligt..
 
Laatst bewerkt:
Als je er niet uit komt kun je je HTML bron laten zien, dan kunnen wij er ook naar kijken ;)
 
Hoewel unescape() het grootste deel van de characters vertaald (na rawurlencode in PHP te hebben gebruikt), blijft 1 teken verkeerd staan. %u2022 zou vertaald moeten worden naar een bolletje (•), maar dit gebeurt niet. Andere characters (ë ê ø) werken echter wel.

Bij mijn vorige methode (met de textarea) werd dit teken wel goed omgezet. Enig idee hoe dit komt?

EDIT: 2x unescape()n werkt wel. Probleem zit hem erin dat bij het opslaan van het bestand de source escape()d word voor het XmlHttpRequest. Vervolgens komt het grootste deel van de tekens 'normaal' in de database te staan, maar een aantal, zoals het bolletje, dus niet. Hierdoor word dat teken voor de 2e keer gecodeerd (met escape() tijdens het opslaan en rawurlencode() voor het inladen) waardoor je de tekst ook weer 2x moet unescapen. Hoe dan ook, dit lost het (alweer) op.

Bedankt voor je hulp :)
 
Laatst bewerkt:
Je moet geen escape() gebruiken. escape() werkt niet met unicode. Gebruik in plaats daarvan encodeURIComponent(). Door twee keer unescape() te gebruiken kun je in de tekst geen % tekens gebruiken. Niet veilig in ieder geval. Had ik misschien meteen iets duidleijker over moeten zijn.

Gebruik overigens ook geen unescape(), gebruik decodeURIComponent(). De oude escape() en unescape() zijn niet unicode veilig. De nieuwe encodeURIComponent() en decodeURIComponent wel. Je hebt overigens ook encodeURI(), maar encodeURIComponent() codeert net wat meer tekens.

:thumb:
 
Laatst bewerkt:
Whoa, even alles op een rijtje:

- Javascript codeert speciale tekens (encodeURIComponent)
- HTML pagina word met een XHR naar een PHP bestand gestuurd
- PHP bestand zet de content in een mysql record (addslashes)

- PHP leest de pagina uit het mysql record (stripslashes)
- Javascript decodeert de speciale tekens (decodeURIComponent)

Heb ik het zo goed? Eerst gebruikte ik dus Escape voor het eerste punt. Ik probeer eens wat.

Merk op dat een % teken gewoon werkt. unescape laat een los procent teken onaangeraakt. hier zijn dus geen problemen mee totzover.

EDIT: De Javascript functies encodeURIComponent en decodeURIComponent zorgen ervoor dat er geen tekst word ingeladen. Dit is ook het enige dat is aangepast.
 
Laatst bewerkt:
nee klopt, maar stel dat iemand om een of andere reden %30 schrijft komt er een raar teken te staan. Het is niet gebruikelijk dat het gebeurt, maar het kan.

Overigens zou ik niet addslashes en stripslahses gebruiken, deze zijn ook al niet meer 100% veilig. mysql_real_escape_string() is de huidige standaard.
http://nl3.php.net/manual/en/function.mysql-real-escape-string.php
Hier heb je wel een openstaande mysql connectie voor nodig maar aangezien je toch met mysql bezig bent moet dat geen probleem zijn. Voor de rest ziet het er goed uit.

Mocht je in PHP hetzelfe willen doen als de javascript functies encodeURIComponent()/decodeURIComponent() dan heb je daarvoor de php functies rawurlencode() en rawurldecode() :)


De Javascript functies encodeURIComponent en decodeURIComponent zorgen ervoor dat er geen tekst word ingeladen. Dit is ook het enige dat is aangepast.
Dan denk ik toch dat er nu ergens een syntax of spelfout zit. encodeURIComponent() verschilt namelijk nauwelijks van escape(), het is alleen wel unicode veilig. Als je je HTML bron laat zien kunnen we dat uitsluiten. Het kan ook nog met je XHR te maken hebben, ik neem aan dat het een POST request is? Maar ik kan eigenlijk alleen maar gokken zonder de HTML bron te zien :p
 
Hmm, ik zie inderdaad dat een %30 wordt vertaald naar een 0... Niet zo goed voor een WYSIWYG editor, of het nou vaak voorkomt of niet, alle fouten uitsluiten.

Ik zal mysql_real_escape_string eens gaan gebruiken, ik zie geen tegengestelde functie hiervan zoals add/strip slashes, gebruik ik deze functie dan ook alleen tijdens het opslaan? Of moet ik tijdens het inladen ook nog iets decoderen?

Ik zal even een deel van de (huidige, werkende) source plaatsen, mocht het duidelijker zijn:

Script 1: JS functie om op te slaan (aan de xhr_post functie is niets mis, staan extern gedefineerd):
Code:
var work=document.getElementById('workfield').contentWindow.document.body.innerHTML;
	if(xhr_post("savework.php","work="+escape(work).replace(/\+/g,'%2B'),"save_handler")){ //replace functie om de plusjes om te zetten, dit doet escape() niet
		icm("saving...",1); //toont een berichtje op de pagina
	}
Notitie op de xhr_post functie: 1e argument geeft de URL aan, 2e de parameters en het 3e argument geeft aan welke functie word uitgevoerd wanneer het request voltooid is (save_handler doet niets meer dan een bericht weergeven dat het bestand goed is opgeslagen).

Script 2: PHP bestand dat de net verstuurde content in de DB zet:
Code:
<?
include("connect.php");
$data=addslashes($_REQUEST['work']);
$db_query="UPDATE editor SET body='".$data."' WHERE title='nameless'";
$db_result=mysql_query($db_query) or trigger_error( mysql_error() );

if($db_result){
echo "1";
}
else{
echo "0";
}
?>

Dan nu het inladen van een document:

Script 3: PHP bestand dat de content van de pagina opent, die word aangeroepen met een XHR (method GET):
Code:
<?php
include("connect.php");
$db_query="SELECT * FROM editor WHERE title='nameless'";
$db_result=mysql_query($db_query) or trigger_error( mysql_error() );
$dat=mysql_fetch_array($db_result);
echo rawurlencode(stripslashes($dat['body']));
?>

Script 4: JS dat word gebruikt wanneer XHR is voltooid, var xhr is het xhr object:
Code:
setTimeout("document.getElementById('workfield').contentWindow.document.body.innerHTML=unescape(unescape(xhr.responseText))",0);
		document.getElementById("workfield").contentWindow.focus();

Dit is dus wat ik nu gebruik. Alles werkt goed tenzij er inderdaad een %30 oid word gebruikt. Wat moet ik nu pressies aanpassen om alles goed te laten werken? Volgens mij begreep ik je net verkeerd.

EDIT:
Oke, ik heb het werkend denk ik. Voordat ik met JS de content verstuur gebruik ik inderdaad encodeURIComponent() over de inhoud van het iframe heen.
PHP gooit er nog eens addslashes() overheen (nu nog wel, in ieder geval) maar tijdens het openen word stripslashes() gebruikt dus deze worden 'opgeheven.
Als ik nu decodeURIComponent gebruik in JS bij het inladen krijg ik een lege pagina te zien. Echter, als ik de onaangepaste xhr.responseText gebruik werkt alles perfect. Oplossing is dus om alleen encodeURIComponent te gebruiken en decodeURIComponent achterwege te laten.

Speciale tekens werken, letters met accent, symbolen, ook %30 ;)

Alweer heel erg bedankt voor je hulp :) Ik neem aan dat alles nu gewoon klopt en zo goed mogelijk is? Anders hoor ik het graag.
 
Laatst bewerkt:
/edit: Ik zie dat je al wat hebt aangepast, maar het zou zonde zijn om nu mijn bericht te verwijderen :P En het deel over MySQL en stripslashes is nog wel nuttig :thumb:

Als je data door mysql_real_escape_string hebt gehaald en in je database hebt opgeslagen hoef (of "mag" eigenlijk) je deze niet te decoden met stripslashes nadat je het weer uit de database haalt. Dat doet MySQL al voor je.

Ik zou dit vervangen in script1:
Code:
xhr_post("savework.php","work="+escape(work).replace(/\+/g,'%2B'),"save_handler")
Door dit:
Code:
xhr_post("savework.php","work="+encodeURIComponent(work),"save_handler")

Dit hang ook af van de xhr_post functie trouwens, want de POST data hoort url-encoded te zijn voordat je het naar de server stuurt. Als het dus letterlijk zo naar de server wordt gestuurd komt het ongecodeerd aan in php. Kun je de xhr_post() functie laten zien?

Maargoed, bij bovenstaande verandering hoort ook deze. Dit vervangen in script 4:
Code:
unescape(unescape(xhr.responseText))
Door dit:
Code:
decodeURIComponent(xhr.responseText)




Ik geloof trouwens niet dat het nodig is om in php (script 3) de output door rawurlencode te halen om hem vervolgens weer te decoderen in script 4 met decodeURIComponent. Ik denk dat je beide stappen weg kunt laten. En stripslashes hoeft (mag) niet nadat je de data uit de MySQL database hebt gehaald. Dus ik zou zelf proberen om in script 3 dit te vervangen:
Code:
echo rawurlencode(stripslashes($dat['body']));
door dit:
Code:
echo $dat['body'];

En in script 4 in plaats van bovengenoemde verandering dit vervangen:
Code:
unescape(unescape(xhr.responseText))
Door dit:
Code:
xhr.responseText

Mocht dat fout gaan zou ik rawurlencode (script 3) en decodeURIComponent (script 4) weer terug zetten :p
 
Laatst bewerkt:
Het klopt dat het onlogisch is om iets te coderen en direct weer te unescapen. In principe hoeft er nauwelijks wat gecodeerd te worden. Ware het niet dat ik wat problemen heb met PHP die speciale characters niet weergeeft. Oplossing daarvoor is door de charset in de header te veranderen, maar op sommige host kun je dat niet omdat de headers gedeeltelijk worden overschreven. Vandaar dat ik wil dat de content die PHP moet echoen is gecodeerd. Maar dat is weer een ander verhaal, terug ontopic.

Ik heb dus inderdaad encodeURIComponent toegepast, zoals je aangaf, het PHP bestand gebruikt geen rawurlencode meer (wel stripslashes, anders blijven de slashes staan en klopt de HTML source niet meer... ik weet niet hoe je bedoeld dat dat niet hoeft/mag??). decodeURIComponent laat ik dus ook weg, en dan werkt alles wel.

xhr_post functie veranderd niks aan de parameters, en codeert ook niks. Je moet dus inderdaad handmatig encodeURIComponent toepassen.

Maar hoe bedoelde je dat nou met het weglaten van stripslashes? Vrijwel alle HTML opmaak verdwijnt dan en op plaatsen waar een ' staat word /' weergegeven. Ik denk toch echt dat deze functie nodig is (tenzij ik gebruik ga maken van mysql_real_escape_string natuurlijk).
 
Laatst bewerkt:
Nouja, stel je hebt de volgende string:
Code:
Moeder's mooiste heet "Jaap" en nou zet ik twee backslash'en neer: \\

Wanner je dit door mysql_real_escape_string() (of stripslashes()) haalt is het zo geworden:
Code:
Moeder\'s mooiste heet \"Jaap\" en nou zet ik twee backslash\'en neer: \\\\

Maar nu sla je het op in de MySQL database en vraag je het weer op. De waarde die je nu uit de database krijgt is dit:
Code:
Moeder's mooiste heet "Jaap" en nou zet ik twee backslash'en neer: \\

Haal je dit nu door stripslashes dan krijg je dit:
Code:
Moeder's mooiste heet "Jaap" en nou zet ik twee backslash'en neer: \
Duidelijk gelogen en niet wat je wilt. Zodra je iets uit de MySQL database opvraagt wordt alles dus al als het ware door stripslashes gehaald. Doe je dat nog een keer dan gaat het fout.

Wat wél kan is dat magic quotes aan staat en dan is de waarde van $_REQUEST['work'] in script 2 al automatisch door addslashes gehaald door PHP. In dat geval zorg je met de stripslashes in script 3 dus dat dat ongedaan wordt gemaakt. Dat hoor je eigenlijk al in script 2 ongedaan te maken. Als het kan, zet dan magic quotes uit. Maat meestal kan dat niet. Ik zou even het PHP.net artikel over magic quotes lezen:
http://nl2.php.net/manual/en/security.magicquotes.what.php
http://nl2.php.net/manual/en/info.configuration.php#ini.magic-quotes-gpc
 
Laatst bewerkt:
Hmm? Als ik addslashes gebruik en ik kijk in de database zie ik bij elke slash en quote een slash erbij staan:

<span class=\"Apple-style-span\" style=\"font-size...

(ik zie het altijd handige class="apple-style-span" even door de vingers)

In dit geval moet ik dus wel stripslashes gebruiken.

Even het artikel over de magic quotes eens lezen dan.

EDIT: Volgens php.net staat deze magic quotes functie "defaults to on in PHP". Houd dus inderdaad in dat er 2x slashes op worden gezet.
Deze instellingen moet je blijkbaar in de "settings in php.ini" aanpassen, iets dat ik niet kan bij mijn huidige host.

Oplossing lijkt me zo om te controleren of de magic quotes aanstaan en op basis daarvan kijken of je nog eens addslashes moet gebruiken:

PHP:
$data=$_REQUEST['work'];
if(!get_magic_quotes_gpc()){
    $data=addslashes($data);
}

EDIT2: Werkt perfect! Nogmaals bedankt voor je hulp en snelle reacties :)
 
Laatst bewerkt:
Ja, dat kan, behalve dat het beter is om mysql_real_escape string te gebruiken. addslashes is niet 100% veilig. Daarom kun je het beter omdraaien en de string juist door stripslashes halen als magic_quotes_gpc aan staat en het vervolgens door mysql_real_escape_string() te halen:

PHP:
$data = $_REQUEST['work'];
if(get_magic_quotes_gpc())
{
    $data = stripslashes($data);
}
$data = mysql_real_escape_string($data);

Maar in beide gevallen hoef je het nu niet meer door stripslashes te halen nadat je het uit de MySQL database hebt opgehaald :thumb:
 
Oke, ik zie atm geen verandering, maar ik neem aan dat ik zo wat veiliger zit.

Overigens, 1 teken dat nu verloren gaat, is een backslash. Enig idee hoe ik dit kan voorkomen?
De backslash word opgeslagen in de database, maar bij het inladen gaat het teken verloren.

EDIT: nvm, werkt alweer, zeker niet goed geuploaded.

Nogmaals bedankt voor je reacties ;)
Alles werkt perfect!
 
Laatst bewerkt:
Status
Niet open voor verdere reacties.

Nieuwste berichten

Terug
Bovenaan Onderaan