blob: 0762b2e2c5efa5c82bd94e3540cbc118201e992a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Implementation of Prototype's Insertion class with Dojo
*
* @version $Id$
*/
dojo.provide("cocoon.ajax.insertion");
dojo.require("dojo.dom");
cocoon.ajax.insertion = {};
dojo.lang.mixin(cocoon.ajax.insertion, {
/**
* Inserts before the reference node
*/
before: function (refElt, content) {
return cocoon.ajax.insertionHelper.insert(refElt, content, function(refElt, newElt) {
refElt.parentNode.insertBefore(newElt, refElt);
});
},
/**
* Inserts after the reference node
*/
after: function (refElt, content) {
return cocoon.ajax.insertionHelper.insert(refElt, content, function(refElt, newElt) {
// There's no node.insertAfter...
if (refElt.nextSibling) {
refElt.parentNode.insertBefore(newElt, refElt.nextSibling);
} else {
refElt.parentNode.appendChild(newElt);
}
});
},
/**
* Inserts at the top of the reference node children (i.e. as the first child)
*/
top: function (refElt, content) {
return cocoon.ajax.insertionHelper.insert(refElt, content, function(refElt, newElt) {
if (refElt.firstChild) {
refElt.insertBefore(newElt, refElt.firstChild);
} else {
refElt.appendChild(newElt);
}
})
},
/**
* Inserts at the bottom of the reference node children (i.e. as the last child)
*/
bottom: function (refElt, content) {
return cocoon.ajax.insertionHelper.insert(refElt, content, function(refElt, newElt) {
refElt.appendChild(newElt);
})
},
/**
* Inserts as the contents of the reference node (i.e. replaces all children)
*/
inside: function (refElt, content) {
return cocoon.ajax.insertionHelper.insert(refElt, content, function(refElt, newElt) {
// Destroy and remove all children
while (refElt.hasChildNodes()) {
var firstChild = refElt.firstChild;
if (firstChild.nodeType == dojo.dom.ELEMENT_NODE) {
cocoon.ajax.insertionHelper.destroy(firstChild);
}
refElt.removeChild(firstChild);
}
// Insert the new one
refElt.appendChild(newElt);
})
},
/**
* Replaces the reference element
*/
replace: function (refElt, content) {
return cocoon.ajax.insertionHelper.insert(refElt, content, function(refElt, newElt) {
refElt.parentNode.replaceChild(newElt, refElt);
cocoon.ajax.insertionHelper.destroy(refElt);
})
}
});
cocoon.ajax.insertionHelper = {};
dojo.lang.mixin(cocoon.ajax.insertionHelper, {
/**
* Imports an element into a document, taking care of using the correct implementation
* so that the browser interprets it as displayable XML.
*
* Any <script> in the imported node are collected and for post-import evaluation
* FIXME: only inline script are considered, but not <script src="">
*
* @return { element: <em>imported_element</em>, scripts: <em>array_of_script_text</em> }
*/
importNode: function(node, targetDoc) {
if(node.xml || dojo.lang.isString(node)) {
// IE or text
var text = dojo.lang.isString(node) ? node : node.xml;
var div = targetDoc.createElement("DIV");
// Code below heavily inspired by the Prototype library
var scriptExpr = "(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)";
var textWithoutScripts = text.replace(new RegExp(scriptExpr, 'img'), '');
// Internet Explorer can't handle empty <textarea .../> elements, so replace
// them with <textarea></textarea>
var textareaExpr = "(<textarea[^<]*)\/>";
var textAreaMatches = textWithoutScripts.match(textareaExpr);
var textFixed = textWithoutScripts.replace(new RegExp(textareaExpr, 'img'), "$1></textarea>");
// Update screen with removed scripts
div.innerHTML = textFixed;
var matchAll = new RegExp(scriptExpr, 'img');
var matchOne = new RegExp(scriptExpr, 'im');
var allMatches = text.match(matchAll);
var scripts = new Array();
if (allMatches) {
for (var i = 0; i < allMatches.length; i++) {
scripts.push(allMatches[i].match(matchOne)[1]);
}
}
return { element: dojo.dom.getFirstChildElement(div), scripts: scripts };
} else {
var scripts = new Array();
var element = this._importDomNode(node, targetDoc, scripts);
return { element: element, scripts: scripts }
}
},
/**
* DOM implementation of importNode, recursively creating nodes.
* Scripts are collected in the "scripts" parameter
*/
_importDomNode: function(node, targetDoc, scripts) {
switch(node.nodeType) {
case dojo.dom.ELEMENT_NODE:
if (node.nodeName.toLowerCase() == "script") {
// Collect scripts
scripts.push(node.firstChild && node.firstChild.nodeValue);
return;
}
var element = targetDoc.createElement(node.nodeName);
//var element = targetDoc.createElementNS(node.namespaceURI, node.nodeName);
var attrs = node.attributes;
for (var i = 0; i < attrs.length; i++) {
var attr = attrs[i];
element.setAttribute(attr.nodeName, attr.nodeValue);
//element.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.nodeValue);
}
var children = node.childNodes;
for (var j = 0; j < children.length; j++) {
var imported = this._importDomNode(children[j], targetDoc, scripts);
if (imported) element.appendChild(imported);
}
return element;
break;
case dojo.dom.TEXT_NODE:
return targetDoc.createTextNode(node.nodeValue);
break;
case dojo.dom.CDATA_SECTION_NODE:
return targetDoc.createTextNode(node.nodeValue);
break;
}
},
_runScripts: function(imported) {
// Evaluate scripts
for (var i = 0; i < imported.scripts.length; i++) {
eval(imported.scripts[i]);
}
},
insert: function(refElt, content, insertFunc) {
refElt = dojo.byId(refElt, content);
var imports = this.importNode(content, refElt.ownerDocument);
insertFunc(refElt, imports.element);
this._runScripts(imports);
return this.parseDojoWidgets(imports.element);
},
destroy: function(element) {
var widget = dojo.widget.byNode(element);
if (widget) {
// Dojo will destroy all its children
widget.destroy(true, true);
} else {
// Recurse until we eventually find a widget
var children = element.childNodes;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.nodeType == dojo.dom.ELEMENT_NODE) {
this.destroy(child);
}
}
}
},
parseDojoWidgets: function(element) {
// Find a parent widget (if any) so that Dojo can maintain its widget tree
var parentWidget = this.findParentWidget(element);
var parser = new dojo.xml.Parse();
// FIXME: parser.parseElement() parses the _children_ of an element, whereas we want here
// the element itself to be parsed. Parsing its parent is not an option as we don't want
// to parse the siblings. So place it in a temporary div that we'll trash afterwards.
var div = document.createElement("DIV");
element.parentNode.replaceChild(div, element);
div.appendChild(element);
var frag = parser.parseElement(div, null, true);
dojo.widget.getParser().createComponents(frag, parentWidget);
// Get again the first child of the div, which may no more be the original one
// if it's a widget
element = div.firstChild;
div.parentNode.replaceChild(element, div);
parentWidget && parentWidget.onResized();
return element;
},
findParentWidget: function(element) {
var parent = element.parentNode;
var widget;
while (parent && !widget) {
var widget = dojo.widget.byNode(parent);
if (widget) {
return widget;
}
parent = parent.parentNode;
}
}
});