diff --git a/README.md b/README.md index d76f1e9..ee32bc5 100644 --- a/README.md +++ b/README.md @@ -10,5 +10,23 @@ HInclude is declarative client-side inclusion for the Web; it allows easy composition of Web pages using the browser -- making your pages more modular, more cacheable, and easier to maintain. +You can run javascript code after loading 'hx:include' inserting the +javascript code inside the tag + + + +example.. + + + //...// + + + + +in the particular case where the content has the JavaScript code that is +executed before 'hinclude' finish on its own, for example widget + See [the demo page](http://mnot.github.com/hinclude/) for documentation and examples. + +in the particular case where the content has the JavaScript code that is executed before 'hinclude' finish on its own, for example widget diff --git a/hinclude.js b/hinclude.js index 4ae83d9..89fda07 100644 --- a/hinclude.js +++ b/hinclude.js @@ -26,8 +26,8 @@ SOFTWARE. See http://mnot.github.com/hinclude/ for documentation. */ -/*jslint indent: 2, browser: true, vars: true, nomen: true */ -/*global alert, ActiveXObject */ +/*jslint indent: 2, browser: true, vars: true, nomen: true, plusplus: true, evil: true, regexp: true */ +/*global alert, ActiveXObject, DOMParser, XMLSerializer */ var hinclude; @@ -37,11 +37,14 @@ var hinclude; hinclude = { classprefix: "include_", + move_head_to_document: true, // moved head script into document head + remove_js: true, // removes script by content set_content_async: function (element, req) { if (req.readyState === 4) { if (req.status === 200 || req.status === 304) { element.innerHTML = req.responseText; + hinclude.hinclude_check_content(element, req.responseText); } element.className = hinclude.classprefix + req.status; } @@ -59,10 +62,14 @@ var hinclude; }, show_buffered_content: function () { + if (hinclude.isEmpty(hinclude.buffer)) { + return false; + } + var include; while (hinclude.buffer.length > 0) { - var include = hinclude.buffer.pop(); + include = hinclude.buffer.pop(); if (include[1].status === 200 || include[1].status === 304) { - include[0].innerHTML = include[1].responseText; + hinclude.hinclude_check_content(include, include[1].responseText); } include[0].className = hinclude.classprefix + include[1].status; } @@ -73,11 +80,11 @@ var hinclude; run: function () { var i = 0; var mode = this.get_meta("include_mode", "buffered"); - var callback = function (element, req) {}; this.includes = document.getElementsByTagName("hx:include"); if (this.includes.length === 0) { // remove ns for IE this.includes = document.getElementsByTagName("include"); } + var callback; if (mode === "async") { callback = this.set_content_async; } else if (mode === "buffered") { @@ -91,6 +98,266 @@ var hinclude; } }, + // convert text into xml node + hinclude_xml_parser_content: function (content) { + var parsed_document = false; + if (!hinclude.isEmpty(content)) { + if (window.ActiveXObject) {// for Internet Explorer + parsed_document = new ActiveXObject('Microsoft.XMLDOM'); + parsed_document.async = 'false'; + parsed_document.loadXML(content); + if (parsed_document.parseError.errorCode !== 0) { + parsed_document = false; + } + } else { + var parser = new DOMParser(); + parsed_document = parser.parseFromString(content, 'text/xml'); + if (parsed_document.getElementsByTagName("parsererror").length > 0) { + parsed_document = false; + } + } + } + return parsed_document; + }, + + // verification content hinclude + hinclude_check_content: function (include, content) { + var parsed_document = this.hinclude_xml_parser_content(content); + this.hinclude_check_head_script(parsed_document); + this.move_html_to_hinclude(include, parsed_document, content); + var js_onload = this.hinclude_check_onload_body(parsed_document); + var js_code = this.hinclude_check_js_code(include); + this.run_hinclude_js(js_onload, js_code); + this.hinclude_check_child_include(include); + }, + + // verificarion exist head script + hinclude_check_head_script: function (parsed_document) { + //xml document + if (!hinclude.isEmpty(parsed_document)) { + var head = parsed_document.getElementsByTagName('head'); + if (head.length > 0) { + var script = head[0].getElementsByTagName('script'); + if (!hinclude.isEmpty(script)) { + this.hinclude_move_head_script_to_document(script[0]); + } + } + } + }, + + // verification exist onload event + hinclude_check_onload_body: function (parsed_document) { + //xml document + if (!hinclude.isEmpty(parsed_document)) { + var body = parsed_document.getElementsByTagName('body'); + var onload = false; + if (body.length > 0) { + if (!hinclude.isEmpty(body[0].getAttribute('onload'))) { + onload = body[0].getAttribute('onload'); + } + } + return onload; + } + return ''; + }, + + // moved head script into document head + hinclude_move_head_script_to_document: function (script) { + if (script && hinclude.move_head_to_document) { + var document_head = document.getElementsByTagName('head')[0]; + var document_script = document.createElement('script'); + document_script.type = 'text/javascript'; + try { + document_script.innerHTML = script.textContent; + } catch (e) { + // Internet Explorer + document_script.text = script.text; + } + document_head.appendChild(document_script); + script.parentNode.removeChild(script); + } + }, + + // inserts html content into hinclude + move_html_to_hinclude: function (include, parsed_document, content) { + var string = ''; + if (!hinclude.isEmpty(parsed_document)) { + string = this.xml_to_string(parsed_document); + } else if (!hinclude.isEmpty(content)) { + string = content; + } + if (this.detectIe7_8()) { + include[0].appendChild(this.fixInnerHtml(string)); + } else { + if (!hinclude.isEmpty(include[0])) { + include[0].innerHTML = string; + } else if (!hinclude.isEmpty(include)) { + include.innerHTML = string; + } + } + }, + + fixInnerHtml: function (value) { + var new_element; + new_element = document.createElement('div'); + new_element.innerHTML = value; + return new_element; + }, + + // convert xml node into string + xml_to_string: function (parsed_document) { + try { + // Gecko-based browsers, Safari, Opera. + var serialize = (new XMLSerializer()).serializeToString(parsed_document); + //fix strip closed tag script + serialize = this.fix_strip_closed_tag(serialize); + return serialize; + } catch (e1) { + try { + // Internet Explorer. + return parsed_document.xml; + } catch (e2) { + //Strange Browser ?? + alert('Xmlserializer not supported'); + } + } + return false; + }, + + fix_strip_closed_tag: function (content) { + var tags = content.match(/<[^>]*>/g); + if (tags.length > 0) { + var t = 0; + var tag; + var replaced; + for (t; t < tags.length; t += 1) { + tag = tags[t]; + replaced = tag.replace(/<([a-zA-Z]+)([\s\S]*?)(\/|\s\/)>/g, '<$1$2>'); + //verify correct replaced + if (tag !== replaced) { + content = content.replace(tag, replaced); + } + } + return content; + } + return content; + }, + + isEmpty: function (value) { + if (value === null || value === undefined) { return true; } + if (value.length && value.length > 0) { return false; } + if (value.length === 0) { return true; } + var type = typeof value; + if (type === 'object') { + if (value.getElementsByTagName('html')) { return false; } + var key; + for (key in value) { + if (value.hasOwnProperty(key)) { + return false; + } + } + } + return true; + }, + + load_js_src_from_content: function (items, iteration) { + if (!iteration) { iteration = 0; } + if (items[iteration]) { + this.move_jsfile_to_document( + items[iteration], + function () { + hinclude.load_js_src_from_content(items, iteration + 1); + } + ); + } + }, + + move_jsfile_to_document: function (js_src, callback) { + if (js_src) { + var document_head = document.getElementsByTagName('head')[0]; + var document_script; + document_script = document.createElement('script'); + document_script.type = 'text/javascript'; + document_script.src = js_src; + if (callback) { + document_script.onreadystatechange = function () { + if (this.readyState === 'loaded') { callback(); } + }; + document_script.onload = callback; + } + document_head.appendChild(document_script); + } + }, + + // verification exists child hinclude + child_includes: [], + hinclude_check_child_include: function (include) { + if (!hinclude.isEmpty(include)) { + var i = 0; + var mode = this.get_meta("include_mode", "buffered"); + this.child_includes = include[0].getElementsByTagName("hx:include"); + if (this.child_includes.length === 0) { // remove ns for IE + this.child_includes = include[0].getElementsByTagName("include"); + } + var callback; + if (mode === "async") { + callback = this.set_content_async; + } else if (mode === "buffered") { + callback = this.set_content_buffered; + var timeout = this.get_meta("include_timeout", 2.5) * 1000; + setTimeout(hinclude.show_buffered_content, timeout); + } + for (i; i < this.child_includes.length; i += 1) { + this.include(this.child_includes[i], this.child_includes[i].getAttribute("src"), this.child_includes[i].getAttribute("media"), callback); + } + } + }, + + // verification exists scripts into content + hinclude_check_js_code: function (include) { + var js_code = ''; + if (!hinclude.isEmpty(include)) { + var js = include[0].getElementsByTagName("script"); + if (js.length > 0) { + var code = ''; + var i = 0; + var js_src = []; + for (i; i < js.length; i++) { + if (js[i].src) { + js_src.push(js[i].src); + } else { + code = js[i].innerHTML; + js_code = js_code + code; + } + } + this.load_js_src_from_content(js_src); + this.hinclude_remove_tag_script(js); + } + } + return js_code; + }, + + // removes script by content + hinclude_remove_tag_script: function (js) { + if (!hinclude.isEmpty(js) && hinclude.remove_js) { + var i = 0; + for (i; i < js.length; i++) { + js[i].parentNode.removeChild(js[i]); + i--; + } + } + }, + + // execute code js + run_hinclude_js: function (js_onload, js_code) { + if (!hinclude.isEmpty(js_code)) { + eval(js_code); + } + if (!hinclude.isEmpty(js_onload)) { + eval(js_onload); + } + }, + include: function (element, url, media, incl_cb) { if (media && window.matchMedia && !window.matchMedia(media).matches) { return; @@ -132,8 +399,8 @@ var hinclude; refresh: function (element_id) { var i = 0; - var mode = this.get_meta("include_mode", "buffered"); - var callback = function (element, req) {}; + //var mode = this.get_meta("include_mode", "buffered"); + var callback; callback = this.set_content_buffered; for (i; i < this.includes.length; i += 1) { if (this.includes[i].getAttribute("id") === element_id) { @@ -143,17 +410,27 @@ var hinclude; }, get_meta: function (name, value_default) { - var m = 0; var metas = document.getElementsByTagName("meta"); - for (m; m < metas.length; m += 1) { - var meta_name = metas[m].getAttribute("name"); - if (meta_name === name) { - return metas[m].getAttribute("content"); + if (!hinclude.isEmpty(metas)) { + var m = 0; + var meta_name; + for (m; m < metas.length; m += 1) { + meta_name = metas[m].getAttribute("name"); + if (meta_name === name) { + return metas[m].getAttribute("content"); + } } } return value_default; }, + detectIe7_8: function () { + if ((document.all && !document.querySelector) || (document.all && document.querySelector && !document.addEventListener)) { + return true; + } + return false; + }, + /* * (c)2006 Dean Edwards/Matthias Miller/John Resig * Special thanks to Dan Webb's domready.js Prototype extension @@ -169,7 +446,9 @@ var hinclude; var init = function () { var i = 0; // quit if this function has already been called - if (hinclude.addDOMLoadEvent.done) {return; } + if (hinclude.addDOMLoadEvent.done) { + return; + } hinclude.addDOMLoadEvent.done = true; if (window.__load_timer) { clearInterval(window.__load_timer); diff --git a/test/Makefile b/test/Makefile index ea72b08..a2c1c88 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,9 +1,12 @@ test_port=8081 -test: basic none small_media large_media lint +test: basic advanced none small_media large_media lint basic: server phantomjs basic.js $(test_port) +advanced: server + phantomjs advanced.js $(test_port) + none: server phantomjs none.js $(test_port) diff --git a/test/advanced.js b/test/advanced.js new file mode 100644 index 0000000..b8af73b --- /dev/null +++ b/test/advanced.js @@ -0,0 +1,9 @@ +var runTests = require('./framework.js').runTests; + +var tests = [ + ['#onload', "this onload is executed"], + ['#library', "this library is visible"], + ['#child', "child included"] +]; + +runTests("advanced.html", tests); \ No newline at end of file diff --git a/test/assets/advanced.html b/test/assets/advanced.html new file mode 100644 index 0000000..768220c --- /dev/null +++ b/test/assets/advanced.html @@ -0,0 +1,23 @@ + + + + + HInclude Tests + + + + + + +

hinclude.js test page

+ +

Advanced Include

+ +

+

+

+ + \ No newline at end of file diff --git a/test/assets/child b/test/assets/child new file mode 100644 index 0000000..2e183b9 --- /dev/null +++ b/test/assets/child @@ -0,0 +1 @@ +child included \ No newline at end of file diff --git a/test/assets/child_hinclude.html b/test/assets/child_hinclude.html new file mode 100644 index 0000000..c9f2378 --- /dev/null +++ b/test/assets/child_hinclude.html @@ -0,0 +1,8 @@ + + +Child Test + + + + + \ No newline at end of file diff --git a/test/assets/large b/test/assets/large index e564f59..fc4159f 100644 --- a/test/assets/large +++ b/test/assets/large @@ -1 +1 @@ -Large viewport +Large viewport diff --git a/test/assets/library.html b/test/assets/library.html new file mode 100644 index 0000000..c3ca122 --- /dev/null +++ b/test/assets/library.html @@ -0,0 +1,11 @@ + + +Library Test + + +error + + + + + \ No newline at end of file diff --git a/test/assets/onload.html b/test/assets/onload.html new file mode 100644 index 0000000..d7547fd --- /dev/null +++ b/test/assets/onload.html @@ -0,0 +1,28 @@ + + +onLoad Test + + + + error +
+ + \ No newline at end of file diff --git a/test/assets/sample_cod.js b/test/assets/sample_cod.js new file mode 100644 index 0000000..a363c30 --- /dev/null +++ b/test/assets/sample_cod.js @@ -0,0 +1 @@ +document.getElementById('library').innerText = hinc.library + ' visible'; \ No newline at end of file diff --git a/test/assets/sample_lib.js b/test/assets/sample_lib.js new file mode 100644 index 0000000..1cd40c9 --- /dev/null +++ b/test/assets/sample_lib.js @@ -0,0 +1,3 @@ +hinc = { + library: "this library is" +}; \ No newline at end of file diff --git a/test/assets/small b/test/assets/small index 66de904..d2a3ee0 100644 --- a/test/assets/small +++ b/test/assets/small @@ -1 +1 @@ -Small viewport +Small viewport