JavaScript echo
!No description available for echo. @php.js developers: Please update the function summary text file.
1 2 3 4 56 7 8 9 1011 12 13 14 1516 17 18 19 2021 22 23 24 2526 27 28 29 3031 32 33 34 3536 37 38 39 4041 42 43 44 4546 47 48 49 5051 52 53 54 5556 57 58 59 6061 62 63 64 6566 67 68 69 7071 72 73 74 7576 77 78 79 8081 82 83 84 8586 87 88 89 9091 92 93 94 9596 97 98 99 100101 102 103 104 105106 107 108 109 110111 112 113 114 115116 117 118 119 120121 122 123 124 125126 127 128 129 130131 132 133 134 135136 137 138 139 140141 142 143 144 145146 147 148 149 150151 152 153 154 155156 157 158 159 | function echo () { // !No description available for echo. @php.js developers: Please update the function summary text file. // // version: 1008.1718 // discuss at: http://phpjs.org/functions/echo // + original by: Philip Peterson // + improved by: echo is bad // + improved by: Nate // + revised by: Der Simon (http://innerdom.sourceforge.net/) // + improved by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Eugene Bulkin (http://doubleaw.com/) // + input by: JB // + improved by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Brett Zamir (http://brett-zamir.me) // + bugfixed by: Brett Zamir (http://brett-zamir.me) // % note 1: If browsers start to support DOM Level 3 Load and Save (parsing/serializing), // % note 1: we wouldn't need any such long code (even most of the code below). See // % note 1: link below for a cross-browser implementation in JavaScript. HTML5 might // % note 1: possibly support DOMParser, but that is not presently a standard. // % note 2: Although innerHTML is widely used and may become standard as of HTML5, it is also not ideal for // % note 2: use with a temporary holder before appending to the DOM (as is our last resort below), // % note 2: since it may not work in an XML context // % note 3: Using innerHTML to directly add to the BODY is very dangerous because it will // % note 3: break all pre-existing references to HTMLElements. // * example 1: echo('<div><p>abc</p><p>abc</p></div>'); // * returns 1: undefined var arg = '', argc = arguments.length, argv = arguments, i = 0; var win = this.window; var d = win.document; var ns_xhtml = 'http://www.w3.org/1999/xhtml'; var ns_xul = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; // If we're in a XUL context var holder; var stringToDOM = function (str, parent, ns, container) { var extraNSs = ''; if (ns === ns_xul) { extraNSs = ' xmlns:html="'+ns_xhtml+'"'; } var stringContainer = '<'+container+' xmlns="'+ns+'"'+extraNSs+'>'+str+'</'+container+'>'; if (win.DOMImplementationLS && win.DOMImplementationLS.createLSInput && win.DOMImplementationLS.createLSParser) { // Follows the DOM 3 Load and Save standard, but not // implemented in browsers at present; HTML5 is to standardize on innerHTML, but not for XML (though // possibly will also standardize with DOMParser); in the meantime, to ensure fullest browser support, could // attach http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.js (see http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.xhtml for a simple test file) var lsInput = DOMImplementationLS.createLSInput(); // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default lsInput.stringData = stringContainer; var lsParser = DOMImplementationLS.createLSParser(1, null); // synchronous, no schema type return lsParser.parse(lsInput).firstChild; } else if (win.DOMParser) { // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default try { var fc = new DOMParser().parseFromString(stringContainer, 'text/xml'); if (!fc || !fc.documentElement || fc.documentElement.localName !== 'parsererror' || fc.documentElement.namespaceURI !== 'http://www.mozilla.org/newlayout/xml/parsererror.xml') { return fc.documentElement.firstChild; } // If there's a parsing error, we just continue on } catch(e) { // If there's a parsing error, we just continue on } } else if (win.ActiveXObject) { // We don't bother with a holder in Explorer as it doesn't support namespaces var axo = new ActiveXObject('MSXML2.DOMDocument'); axo.loadXML(str); return axo.documentElement; } /*else if (win.XMLHttpRequest) { // Supposed to work in older Safari var req = new win.XMLHttpRequest; req.open('GET', 'data:application/xml;charset=utf-8,'+encodeURIComponent(str), false); if (req.overrideMimeType) { req.overrideMimeType('application/xml'); } req.send(null); return req.responseXML; }*/ // Document fragment did not work with innerHTML, so we create a temporary element holder // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default //if (d.createElementNS && (d.contentType && d.contentType !== 'text/html')) { // Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways) if (d.createElementNS && // Browser supports the method (d.documentElement.namespaceURI || // We can use if the document is using a namespace d.documentElement.nodeName.toLowerCase() !== 'html' || // We know it's not HTML4 or less, if the tag is not HTML (even if the root namespace is null) (d.contentType && d.contentType !== 'text/html') // We know it's not regular HTML4 or less if this is Mozilla (only browser supporting the attribute) and the content type is something other than text/html; other HTML5 roots (like svg) still have a namespace )) { // Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways); last test is for the sake of being in a pure XML document holder = d.createElementNS(ns, container); } else { holder = d.createElement(container); // Document fragment did not work with innerHTML } holder.innerHTML = str; while (holder.firstChild) { parent.appendChild(holder.firstChild); } return false; // throw 'Your browser does not support DOM parsing as required by echo()'; }; var ieFix = function (node) { if (node.nodeType === 1) { var newNode = d.createElement(node.nodeName); var i, len; if (node.attributes && node.attributes.length > 0) { for (i = 0, len = node.attributes.length; i < len; i++) { newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName)); } } if (node.childNodes && node.childNodes.length > 0) { for (i = 0, len = node.childNodes.length; i < len; i++) { newNode.appendChild(ieFix(node.childNodes[i])); } } return newNode; } else { return d.createTextNode(node.nodeValue); } }; for (i = 0; i < argc; i++ ) { arg = argv[i]; if (this.php_js && this.php_js.ini && this.php_js.ini['phpjs.echo_embedded_vars']) { arg = arg.replace(/(.?)\{\$(.*?)\}/g, function (s, m1, m2) { // We assume for now that embedded variables do not have dollar sign; to add a dollar sign, you currently must use {$$var} (We might change this, however.) // Doesn't cover all cases yet: see http://php.net/manual/en/language.types.string.php#language.types.string.syntax.double if (m1 !== '\\') { return m1+eval(m2); } else { return s; } }); } if (d.appendChild) { if (d.body) { if (win.navigator.appName == 'Microsoft Internet Explorer') { // We unfortunately cannot use feature detection, since this is an IE bug with cloneNode nodes being appended d.body.appendChild(stringToDOM(ieFix(arg))); } else { var unappendedLeft = stringToDOM(arg, d.body, ns_xhtml, 'div').cloneNode(true); // We will not actually append the div tag (just using for providing XHTML namespace by default) if (unappendedLeft) { d.body.appendChild(unappendedLeft); } } } else { d.documentElement.appendChild(stringToDOM(arg, d.documentElement, ns_xul, 'description')); // We will not actually append the description tag (just using for providing XUL namespace by default) } } else if (d.write) { d.write(arg); }/* else { // This could recurse if we ever add print! print(arg); }*/ } } |
Examples
Running
1 | echo('<div><p>abc</p><p>abc</p></div>'); |
Should return
1 | undefined |
Dependencies
No dependencies, you can use this function standalone.
Open syntax issues
php.js uses JsLint to help us keep our code consistent and prevent some common bugs.
Eventually we want all code to pass or at least take into consideration most fixes suggested by JsLint, following this JsLint configuration we’ve decided on.
Authors
Thanks to the following developers, you get to have echo goodness in JavaScript.
JB, I adapted your idea as our last resort, since it may not work in an XML context. I also moved out the bulky code and added support for the common DOMParser or IE-specific parsing--for those who want full standards support, they can add the script referenced in the code to add DOM 3 Load and Save. The problem now though is that one must use the XHTML namespace since the script is giving a chance for the parsing to be done by DOMParser(), etc. for the sake of working with and in XML. We might make that code only execute if an ini is set, thus removing the need for a namespace.
Couldn't you just create a new node, innerHTML the code into it then move all of the children of the new node into body?
@Eugene, thanks for the catch! Fixed in SVN. (p.s. We're hoping to catch such issues automatically in the future with JSLint)
Lines 62 and 132 are missing semicolons after the bracket; as you are setting a var to a function, you must include them or when you pack the code it can trigger errors.
@ Brett Zamir: Noted. I don't mind big comments. Relevant info should be here, and people should know how to use ctrl+f :)
As far as output buffering, I plan to shortly discuss my thoughts on how this, as with other issues, can be solved without too much pain for maintenance with a global instead, but if not, being dependent on echo would be fine by me.
By the way, maybe the function could also test
if (document.body) {
document.body.appendChild(stringToDOM(arg));
}
else {
document.documentElement.appendChild(stringToDOM(arg));
}
to see if there IS a document.body, in case we are in an XML context (like XUL), and if not, append after the last element in the document.
As far as the speed, in my crude tests just now in JavaScript, there seems to be no difference in JavaScript between returning with null and not returning anything. But in PHP there is apparently a slight difference according to the FAQ linked from the PHP documentation: http://www.faqts.com/knowledge_base/view.phtml/aid/1/fid/40 , and the behavior of echo is not to return anything (while print returns 1), so I think there's no reason to return null specifically (takes a little more space in the already big function!) :)
As far as implementing the DOM functions, you're right, there is no place I know of, except serialize() if it handles DOM objects, and DOMDocument::saveXML, DOMDocument::loadXML . (speaking of the DOM in PHP (as in DOMDocument::validate), I'd LOVE to get XML validation in JS somehow).
But the note about the DOM functions was otherwise more just of an interesting aside (if you don't mind my overloading the comments here!).
unfortunately, right now i cannot implement print as rhino already provides it. will have to change the testsuite later on.
out buffering is a great idea. we should first make all functions that print something rely on echo,
and have echo collect a buffer. i can see this working.
does that really effect the speed, yes?
notes removed.
I don't see yet where else we we should implement those dom functions. I have to say I don't work with that stuff on a daily basis.
Sorry, if you did apply my changes for an independent version of print(), this should be changed to also apply the fix for stringToDOM():
[CODE="Javascript"]
if (document.createDocumentFragment && document.createTextNode && document.appendChild) {
stringToDOM(arg);
}[/CODE]
to
[CODE="Javascript"]
if (document.createDocumentFragment && document.createTextNode && document.appendChild) {
document.body.appendChild(stringToDOM(arg));
}[/CODE]
One important thing first...The line
[CODE="Javascript"]stringToDOM(arg);[/CODE]
needs to be changed to:
[CODE="Javascript"]document.body.appendChild(stringToDOM(arg));[/CODE]
Now...print() would be the same with the following differences:
This line at the top:
[CODE="Javascript"]var arg = '', argc = arguments.length, argv = arguments, i = 0;[/CODE]
can be entirely removed in print(). print() should also have "arg" added as an explicit function parameter (unless you want to merge the functions--see below).
To get the marginal speed difference with echo, you don't even need the line "return null" because functions always return undefined when not specified (which is probably a little closer to the PHP behavior if we get picky).
The for loop at the end can be replaced with the following shorter version if you are making an independent version for print():
[CODE="Javascript"]
if (document.createDocumentFragment && document.createTextNode && document.appendChild) {
stringToDOM(arg);
} else if (document.write) {
document.write(arg);
}
return 1;
[/CODE]
Note that if you did decide to have print() call echo() and just let its single argument cycle through the for loop one time, you'd have to still:
1) avoid potential recursion by avoiding the print(arg) call (as I did above for a dedicated version of print())
2) return 1 instead of not returning anything
Also, you can remove the notes to this function now (and for print()).
By the way, I realized that the stringToDOM() function can be stripped down a tiny bit more by changing substr() references to slice() (same behavior with one argument)--and substring() calls could be changed to slice() in cases where the 2nd argument would never be negative, though that would be harder to gauge without going through the code.
After going through the stringToDOM() code a little more, besides the shortening just mentioned, I realized that a little bit more could be done, such as allow namespaces on elements and attributes when parsing to DOM. I'm not sure though when I may have time to get to it (have some deadlines now), but I hope to get to it eventually.
It would be cool if there could be some what we could specify where we wanted the echo to go (e.g., if we didn't want it in the body). You know, I think we should be able to actually implement the PHP output buffer functions by configuring a parameter (as in the global php_js) which allowed the output from such functions to be captured and aggregated (until flushed/cleaned) and then output later or assigned to a variable!
As an extended aside, another great thing about the stringToDOM() and DOMToString() functions is that they could be combined to make a standards-compliant version of the DOM level 3 Load-and-Save module for serialization and parsing (currently not supported in Firefox and probably other browsers).
For example, using the skeleton code within the source at https://bugzilla.mozilla.org/attachment.cgi?id=333875 (for Mozilla bug https://bugzilla.mozilla.org/show_bug.cgi?id=155749 ), they could be used within the code for LSSerializer.prototype.writeToString and LSParser.prototype.parse .
Thus you could parse in any browser in a compliant fashion with something like this:
[CODE="Javascript"]var lsInput = DOMImplementationLS.createLSInput().stringData = '<myXml/>';
var doc = DOMImplementationLS.createLSParser(1, null).parse(lsInput);
[/CODE]
and serialize in any browser with something like this:
[CODE="Javascript"]
var ser = DOMImplementationLS.createLSSerializer();
var str = ser.writeToString(document);[/CODE]
Admittedly it might not be pretty, but it's standard (and can easily be wrapped as with the current Firefox and IE equivalents).
@ der_simon & Brett Zamir: First of all, Der Simon Thank you so much for coming to us and offering your great code to our project. It seems we can put it to good use.
My main concern was that for such a little function, it would be a lot of code. There's no room in PHP.JS for global dependencies so it would really have to be duplicated inside the functions. So a really bloated echo would be the result.
I'm okay with it now because:
- Brett you did a great job trimming it down the the essentials that are important to us.
- I have actually gotten some work done on the public compiler, which will enable people to optionally include echo in their package.
Never ceases to amaze me. The simplest functions sometimes need the biggest chunks of code ;)
Anyway, great effort both.
PS. Can we alias print to echo?
Since Kevin stated it might be possible to use innerDOM, I took it upon myself to check with Der Simon to see if we could release under MIT.
Although it is still somewhat large for the average PHP-JS function, with a few superficial tweaks to the already terse code (such as removing a couple document.all blocks), and by just using one of his functions (the only one needed by echo), we can get something as relatively small as this:
[CODE="Javascript"]function stringToDOM(q){
var d=document;
function r(a){
return a.replace(/\r/g,' ').replace(/\n/g,' ');
}
function s(a){
return a.replace(/&amp;/g,'&').replace(/&gt;/g,'>').replace(/&lt;/g,'<').replace(/&nbsp;/g,' ').replace(/&quot;/g,'"');
}
function t(a){
return a.replace(/ /g,'');
}
function u(a){
var b,c,e,f,g,h,i;
b=d.createDocumentFragment();
c=a.indexOf(' ');
if(c===-1){
b.appendChild(d.createElement(a.toLowerCase()))
}
else{
i=t(a.substring(0,c)).toLowerCase();
a=a.substr(c+1);
b.appendChild(d.createElement(i));
while(a.length){
e=a.indexOf('=');
if(e>=0){
f=t(a.substring(0,e)).toLowerCase();
g=a.indexOf('"');
a=a.substr(g+1);
g=a.indexOf('"');
h=s(a.substring(0,g));
a=a.substr(g+2);
b.lastChild.setAttribute(f,h)
}else{
break
}
}
}
return b
}
function v(a,b,c){
var e,f;
e=b;
c=c.toLowerCase();
f=e.indexOf('</'+c+'>');
a=a.concat(e.substring(0,f));
e=e.substr(f);
while(a.indexOf('<'+c)!=-1){
a=a.substr(a.indexOf('<'+c));
a=a.substr(a.indexOf('>')+1);
e=e.substr(e.indexOf('>')+1);
f=e.indexOf('</'+c+'>');
a=a.concat(e.substring(0,f));
e=e.substr(f)
}
return b.length-e.length
}
function w(a){
var b,c,e,f,g,h,i,j,k,l,m,n,o,p,q;
b=d.createDocumentFragment();
while(a&&a.length){
c=a.indexOf('<');
if(c===-1){
a=s(a);
b.appendChild(d.createTextNode(a));
a=null
}
else if(c){
q=s(a.substring(0,c));
b.appendChild(d.createTextNode(q));
a=a.substr(c)
}
else{
e=a.indexOf('<!--');
if(!e){
f=a.indexOf('-->');
g=a.substring(4,f);
g=s(g);
b.appendChild(d.createComment(g));
a=a.substr(f+3)
}
else{
h=a.indexOf('>');
if(a.substring(h-1,h)==='/'){
i=a.indexOf('/>');
j=a.substring(1,i);
b.appendChild(u(j));
a=a.substr(i+2)
}
else{
k=a.indexOf('>');
l=a.substring(1,k);
m=d.createDocumentFragment();
m.appendChild(u(l));
a=a.substr(k+1);
n=a.substring(0,a.indexOf('</'));
a=a.substr(a.indexOf('</'));
if(n.indexOf('<')!=-1){
o=m.lastChild.nodeName;
p=v(n,a,o);
n=n.concat(a.substring(0,p));
a=a.substr(p)
}
a=a.substr(a.indexOf('>')+1);
m.lastChild.appendChild(w(n));
b.appendChild(m)
}
}
}
}
return b
}
return w(q)
}[/CODE]
I suggest incorporating his useful and standards-compliant method (and also adding print() too), if you were still open to it...
(Maybe it would be even more standards compliant if it checked for createElementNS support, but using the function in XUL still works ok for me)
Hi,
im the author of the innerDOM-Script and I'm happy that anyone really cares about my few lines of code. I'm way out of the programming business, so I can only partly partitiate.
What I couldn't see from my point is: Whould it help to contribiute the innerDOM-Script to your project or not? The php.js page says:
“There's no good place for a package like http://innerdom.sourceforge.net/â€
So if it would be helpful I'm willing to contribute my code.
Regard from Germany
Der Simon
@ Nate: I believe it would upset a lot of peope if we were to use innerHTML (considering "echo is bad"'s comments). innerDOM seems to be a solution, but we can't include it into php.js without changing php.js' nature. And to do that for only echo, would make inpracticle an understatement. We'll have to think some more on the innerDOM thing I guess: maybe we could ask it's author if we can just copy a portion of his code into this function directly. That would:
- work
- not change php.js' standalone-lyness
- make echo a big function ;)
Thank you very much for sharing your thoughts and code on this with us. For now, I've at least comitted your changes.
There are a few problems with this function.
1. There is a misspelling. The code
[CODE="Javascript"]
var txt = document.createTextNode(aarg);
[/CODE]
Should read
[CODE="Javascript"]
var txt = document.createTextNode(arg);
[/CODE]
2. After
[CODE="Javascript"]
docFragment.appendChild(txt);
[/CODE]
you need the line
[CODE="Javascript"]
document.body.appendChild(docFragment);
[/CODE]
I tested it with Firefox, IE, Opera, and Chrome (via wine), and they all worked (on Linux).
3. Like Philip Peterson pointed out, createTextNode() converts HTML to text. I still think that innerHTML() is better because it actually works (and it's fast). Though it might not be practical for this project, you could use the innerDOM script from http://innerdom.sourceforge.net/ to achieve the same effect as innerHTML() and adhere to "standards."
@ Waldo Malqui Silva: Hi there! Good to have you back Waldo! I've changed all the _argos credits to Waldo Malqui Silva.
We track the 'unported' functions in SVN, which can also be viewed here:
http://trac.plutonia.nl/projects/phpjs/browser/trunk/_unported
Everytime we create a new function it's moved from ./_unported to ./functions
I'll be looking forward to seeing some of your excellent work again. And your english i fine btw ;) ciao!
HI, Kevin I'm back from long tiime, I wanna know if is possible 2 things:
1.- Change mi nick (_argos) by my real name (Waldo Malqui Silva) :p
2.- If you have a TODO list of functions to port.
I'm happy to back and see more functions that in my last visit. I promise practice more my english :p
PD: I'm working in a customizable download for the project like mootools 1.11 (packages¿?) based on the PHP functions group :p
Aggh, sorry to clog up the comments, but there's also another problem: using createTextNode and then appending it like that converts all the special characters (e.g. tags) to HTML entities, thus reducing the usability of HTML formatting in echo().
Also, this is a very pressing issue. In Firefox, at the line
document.appendChild(elmt);
the script breaks, because it should be (for Firefox at least)
body.appendChild(elmt);
From Ronsguide.com:
var docFragment = document.createDocumentFragment();
var txt = document.createTextNode("my text node");
docFragment.appendChild(txt);
I guess probably something like that? (note that I am not "echo is bad".)
Perhaps there should be an alias print() which would take one argument? It may also be useful to handle \b things (I think php does this, but I'm not sure), which could be accomplished by inserting some text that appears nowhere else in the document (like [COD_92993] or something) and then modify the .innerHTML of the body by removing that [COD_92993] plus as many characters as there are \b's.
@ echo is bad: I've never used that function, could you tell me:
- this is what the code Should look like?
- your name to include in the comment


Cue
May 13th