c9-core/plugins/c9.preview/static/livecss.js

1027 wiersze
32 KiB
JavaScript

(function() {
var parent = window.opener || window.parent;
if (!parent) return;
var host = (location.search.match(/_c9_host=(.*?)(?:$|&)/) || false)[1];
var id = (location.search.match(/[?&]_c9_id=([a-zA-Z0-9]+)(?:$|&)/) || false)[1];
if (!host || !id) {
var cookies = document.cookie.split(";");
cookies.forEach(function(c) {
var parts = c.split("=");
var key = parts.shift().replace(/^\s+/, "");
if (key == "_c9_host") host = parts.join(";");
else if (key == "_c9_id") id = parts.join(";");
});
if (!host || !id)
return;
}
else {
document.cookie = "_c9_host=" + host;
document.cookie = "_c9_id=" + id;
}
var mainPath = location.href.split("?")[0];
var isHosted = mainPath.indexOf("/preview") == -1;
var importedStyles = {};
var session, reloader;
function init(id) {
if (session)
return console.error("connection alreay established");
reloader = new Reloader(window, console);
window.addEventListener("message", function(e) {
var data = e.data;
// TODO check origin
if (data.id != id || host != "local" && e.origin !== host)
return;
if (data.type == "updatecss") {
reloader.reloadStyleSheet(data.path, data.data);
resize();
}
else if (data.type == "domedits")
editHandler.apply(data.edits);
else if (data.type == "initdom")
editHandler.initDom(data.dom);
else if (data.type == "simpledom") {
var dom = getSimpleDOM();
send({ message: "callback", data: dom, cb: data.cb });
}
else if (data.type == "keys")
setKeys(data.keys);
else if (data.type == "stylerule") {
updateStyleRule(data.url, data.rule);
resize();
}
else if (data.type == "highlight")
highlightByCssQuery(data.query);
else if (data.type == "reload")
window.location.reload();
else if (data.type == "reveal") {
highlightByCssQuery(data.query);
var el = document.querySelector(data.query);
if (el)
(el.scrollIntoViewIfNeeded || el.scrollIntoView).call(el);
}
});
var styles = reloader.getAllStylesheets();
var scripts = reloader.getAllScripts();
session = {
id: id,
styles: styles,
scripts: scripts //@todo
};
setTimeout(function(){
send({
message: "html.ready",
data: {
styles: session.styles.map(function(n){ return n.path }),
scripts: session.scripts.map(function(n){ return n.path }),
href: document.location.pathname.replace(/^\/preview\//, "/")
}
});
}, 1000);
}
function urlToPath(url, base) {
// Absolute Path
if (url.charAt(0) == "/") {
// return url;
}
// Absolute URL
else if (url.match(/^https?:\/\//)) {
var origin = location.origin;
if (url.substr(0, origin.length) == origin) {
url = url.substr(origin.length);
}
// return url;
}
else {
if (!base) base = dirname(location.pathname);
url = join(base, url);
}
if (!isHosted)
url = url.replace(/^\/preview/, "");
return url;
}
function getDataUrl(data){ return "data:text/css," + data.replace(/\n/g, ""); }
function dirname(path){ return path.substr(0, path.lastIndexOf("/")); }
function join(base, path) {
if (path.charAt(0) != "/") {
var parts = path.split("/");
var sparts = base.split("/");
for (var i = 0; i < parts.length; i++) {
if (parts[i] == "..")
sparts.pop();
else
sparts.push(parts[i]);
}
return sparts.join("/");
}
return path;
}
/**** RELOADER ****/
/*global CSSRule*/
function Reloader(win, console) {
function getAllStylesheets(){
var results = [];
var nodes, node, i, path;
// Link tags
nodes = win.document.querySelectorAll("link");
for (i = 0; i < nodes.length; i++) {
node = nodes[i];
path = urlToPath(node.href);
results.push({ path: path, href: node.href, node: node });
findImportedSheets(node, node.sheet, results, dirname(path));
}
// Style tags
nodes = win.document.querySelectorAll("style");
for (i = 0; i < nodes.length; i++) {
node = nodes[i];
if (node.c9__path)
results.push({ path: node.c9__path, href: node.c9__href, node: node });
if (node.sheet)
findImportedSheets(node, node.sheet, results,
dirname(node.c9__path || mainPath));
}
if (win.StyleFix) {
nodes = win.document.querySelectorAll('style[data-href]');
for (i = 0; i < nodes.length; i++) {
var href = urlToPath(nodes[i].getAttribute('data-href'));
results.push({ path: urlToPath(href), href: href, node: nodes[i] });
}
}
return results;
}
function getAllScripts(){
var results = [];
var nodes, node, i, path;
// Link tags
nodes = win.document.querySelectorAll("script[src]");
for (i = 0; i < nodes.length; i++) {
node = nodes[i];
if (node.src.indexOf("/livecss.js") > -1) continue;
path = urlToPath(node.src);
results.push({ path: path, src: node.src, node: node });
}
return results;
}
function findStylesheet(path) {
var results = session.styles; //getAllStylesheets();
return pickBestMatch(path, results, function(item) {
return item.path;
});
}
function findImportedSheets(node, styleSheet, results, base) {
try {
var rules = styleSheet !== null ? styleSheet.cssRules : void 0;
} catch (_error) {
return;
}
if (rules && rules.length) {
var rule;
for (var i = rules.length - 1; i >= 0; i--) {
rule = rules[i];
switch (rule.type) {
case CSSRule.CHARSET_RULE:
continue;
case CSSRule.IMPORT_RULE:
var path = urlToPath(rule.href, base);
if (importedStyles[path]) {
styleSheet.deleteRule(i);
break;
}
results.push({
node: node,
rule: rule,
index: i,
path: path,
href: rule.href
});
findImportedSheets(node, rule.styleSheet, results, dirname(path));
break;
default:
break;
}
}
}
}
function pickBestMatch(path, objects, pathFunc) {
var bestMatch, object, score, _i, _len;
bestMatch = {
score: 0
};
for (_i = 0, _len = objects.length; _i < _len; _i++) {
object = objects[_i];
score = numberOfMatchingSegments(path, pathFunc(object));
if (score > bestMatch.score) {
bestMatch = {
object: object,
score: score
};
}
}
if (bestMatch.score > 0) {
return bestMatch;
} else {
return null;
}
}
function numberOfMatchingSegments(path1, path2) {
var comps1, comps2, eqCount, len;
path1 = path1.replace(/^\/+/, '').toLowerCase();
path2 = path2.replace(/^\/+/, '').toLowerCase();
if (path1 === path2) {
return 10000;
}
comps1 = path1.split('/').reverse();
comps2 = path2.split('/').reverse();
len = Math.min(comps1.length, comps2.length);
eqCount = 0;
while (eqCount < len && comps1[eqCount] === comps2[eqCount]) {
++eqCount;
}
return eqCount;
}
// Normalize Image Paths
function normalizeImagePaths(cssPath, data) {
cssPath = cssPath.match(/^https?:\/\//) // If an absolute url
? dirname(cssPath) // Retrieve the absolute path
: join(mainPath, dirname(cssPath)); // Normalize path, based on main path
var expando = generateUniqueString();
return data.replace(/\burl\s*\("?([^)]*?)"?\)/g, function(match, src) {
if (!src.match(/^https?:\/\//)) { // Ignore absolute paths
var path = join(cssPath, src);
return "url(" + (generateCacheBustUrl(path, expando)) + ")";
} else {
return match;
}
});
}
function generateUniqueString() {
return 'livereload=' + Date.now();
}
function generateCacheBustUrl(url, expando) {
if (!expando)
expando = this.generateUniqueString();
var parts = url.split("#");
var base = parts[0];
var hash = parts[1] || "";
var newBase = base.replace(/(\?|&)livereload=(\d+)/, function(match, sep) {
return "" + sep + expando;
});
if (newBase === base)
newBase += (~base.indexOf("?") ? "&" : "?") + expando;
return newBase + hash;
}
function reloadStyleSheet(path, content) {
// Find Stylesheet
var match = findStylesheet(path);
if (!match) return;
match = match.object;
var replaced, node;
// If Imported Rule
if (match.rule) {
node = document.createElement("style");
node.c9__path = match.path;
node.c9__href = match.href;
// Delete Rule
match.rule.parentStyleSheet.deleteRule(match.index);
delete match.rule;
// Insert style rule
match.node.parentNode.insertBefore(node, match.node);
match.node = node;
// Add to list of replaces import rules
importedStyles[match.path] = match;
replaced = true;
}
// If Link, replace link with style node
else if (match.node.tagName == "LINK") {
node = document.createElement("style");
node.c9__path = match.path;
node.c9__href = match.href;
match.node.parentNode.insertBefore(node, match.node);
match.node.parentNode.removeChild(match.node);
match.node = node;
for (var i = session.styles.length - 1; i >= 0; i--) {
if (session.styles[i].rule && session.styles[i].node == node)
session.styles.splice(i);
}
replaced = true;
}
// Set content
match.node.textContent = normalizeImagePaths(match.href, content);
if (replaced)
findImportedSheets(node, node.sheet, session.styles, dirname(match.path));
}
function reloadImage(path) {
}
return {
reloadStyleSheet: reloadStyleSheet,
reloadImage: reloadImage,
findStylesheet: findStylesheet,
getAllStylesheets: getAllStylesheets,
getAllScripts: getAllScripts
};
}
// Keys
var ckb;
function setKeys(list) {
ckb = {};
list.forEach(function(item) {
var binding = item.binding;
var command = item.command;
var hashId = binding.hashId;
var hash = (ckb[hashId] || (ckb[hashId] = {}));
if (!hash[binding.key]) {
hash[binding.key] = command;
} else {
if (!Array.isArray(hash[binding.key]))
hash[binding.key] = [hash[binding.key]];
hash[binding.key].push(command);
}
}, this);
}
document.addEventListener("keydown", function(e) {
var hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0)
| (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0);
var keys = ckb && ckb[hashId];
var cmd = keys && keys[e.keyCode];
if (cmd) {
send({ message: "exec", command: cmd });
e.preventDefault();
e.stopPropagation();
}
});
window.addEventListener("focus", function(){
send({ message: "focus" });
removeHighlight();
});
// Highlight
var HIGHLIGHT_CLASSNAME = "c9___highlighter";
var last = [], lastHighlight;
function removeHighlight(){
last.forEach(function(item) {
item.destroy();
});
}
function Highlight(div, text, setDisplayName) {
var cover = document.documentElement.appendChild(document.createElement("c9__div"));
cover.style.position = "fixed";
cover.style.zIndex = 10000000000;
cover.style.border = "1px solid rgba(238, 238, 48, 0.5)"; //rgba(128, 128, 128, 0.85)"
cover.style.boxSizing = "border-box";
cover.style.boxShadow = "0 0 20px rgba(0,0,0,0.5)";
cover.style.pointerEvents = "none";
cover.style.color = "black";
cover.className = HIGHLIGHT_CLASSNAME;
cover.innerHTML = "<c9__div><c9__div></c9__div><c9__label></c9__label></c9__div>";
cover.style.background = "transparent"; //"rgba(53, 239, 255, 0.5)";
var child = cover.firstChild;
child.style.whiteSpace = "nowrap";
var arrow = child.firstChild;
var label = child.lastChild;
arrow.style.border = "7px inset blue";
arrow.style.borderColor = "transparent transparent rgb(228, 214, 140) transparent";
arrow.style.position = "absolute";
arrow.style.left = "3px";
arrow.style.top = "-13px";
child.style.position = "absolute";
child.style.bottom = "-20px";
child.style.left = "0";
child.style.fontFamily = "Arial";
child.style.fontSize = "11px";
child.style.background = "rgb(228, 214, 140)"; //"rgb(238, 238, 48)";
child.style.color = "rgb(0, 0, 0)";
child.style.padding = "2px 4px 3px 4px";
child.style.borderRadius = "3px";
child.style.boxShadow = "rgba(0, 0, 0, 0.7) 0px 1px 3px";
label.style.color = child.style.color;
if (setDisplayName) {
text = div.tagName.toLowerCase()
+ (div.id ? "<c9__span style='color:rgb(41, 91, 180)'>#" + div.id + "</c9__span>" : "")
+ (div.className ? "<c9__span style='color:rgb(72, 128, 12)'>." + div.className.replace(/ /g, "") + "</c9__span>" : "");
}
this.resize = function(){
var pos = div.getBoundingClientRect();
cover.style.left = (pos.left - 1) + "px";
cover.style.top = (pos.top - 1) + "px";
cover.style.width = (pos.width + 2) + "px";
cover.style.height = (pos.height + 2) + "px";
label.innerHTML = "<c9__span style='color:maroon'>" + text + "</c9__span> "
+ pos.width + "<c9__span style='color:gray'>px</c9__span>"
+ " x "
+ pos.height + "<c9__span style='color:gray'>px</c9__span>";
};
this.destroy = function(){
if (cover.parentNode)
cover.parentNode.removeChild(cover);
};
this.resize();
}
function highlightByCssQuery(query) {
removeHighlight();
last = [];
if (!query) {
lastHighlight = null;
return;
}
var setDisplayName = query.match(/\[data\-\w+\-id='\d+'\]/) ? true : false;
var nodes = document.querySelectorAll(query);
var len = Math.min(10, nodes.length);
for (var i = len - 1; i >= 0; i--) {
last.push(new Highlight(nodes[i], query, setDisplayName));
}
lastHighlight = { fn: highlightByCssQuery, query: query };
}
function redrawHighlights(){
if (lastHighlight) {
lastHighlight.fn(lastHighlight.query);
}
}
function resize(){
last.forEach(function(item) {
item.resize();
});
showSize();
clearTimeout(timer);
timer = setTimeout(hideSize, 1000);
}
var timer, sizeDiv;
function showSize(){
if (!sizeDiv) {
sizeDiv = document.createElement("div");
sizeDiv.style.position = "absolute";
sizeDiv.style.zIndex = 100000000000;
sizeDiv.style.right = 0;
sizeDiv.style.top = 0;
sizeDiv.style.background = "rgba(255,255,255,0.7)";
sizeDiv.style.color = "#333";
sizeDiv.style.fontFamily = "Arial";
sizeDiv.style.fontSize = "20px";
sizeDiv.style.padding = "2px 6px";
sizeDiv.style.borderRadius = "0 0 0 3px";
}
document.documentElement.appendChild(sizeDiv);
sizeDiv.innerHTML = window.innerWidth + "px \u00D7 " + window.innerHeight + "px";
}
function hideSize(){
if (sizeDiv.parentNode)
document.documentElement.removeChild(sizeDiv);
}
window.addEventListener("resize", resize);
window.document.addEventListener("scroll", resize);
// Editing
/**
* Constructor
* @param {Document} htmlDocument
*/
function DOMEditHandler(htmlDocument) {
this.htmlDocument = htmlDocument;
this.rememberedNodes = null;
this.entityParseParent = htmlDocument.createElement("div");
}
/**
* @private
* Find the first matching element with the specified data-cloud9-id
* @param {string} id
* @return {Element}
*/
DOMEditHandler.prototype._queryBracketsID = function (id) {
if (!id) {
return null;
}
if (this.rememberedNodes && this.rememberedNodes[id]) {
return this.rememberedNodes[id];
}
var results = this.htmlDocument.querySelectorAll("[data-cloud9-id='" + id + "']");
return results && results[0];
};
/**
* @private
* Insert a new child element
* @param {Element} targetElement Parent element already in the document
* @param {Element} childElement New child element
* @param {Object} edit
*/
DOMEditHandler.prototype._insertChildNode = function (targetElement, childElement, edit) {
var before = this._queryBracketsID(edit.beforeID),
after = this._queryBracketsID(edit.afterID);
if (edit.firstChild) {
before = targetElement.firstChild;
} else if (edit.lastChild) {
after = targetElement.lastChild;
}
if (!childElement || !targetElement) {
return console.error("missing tagID");
}
if (before) {
targetElement.insertBefore(childElement, before);
} else if (after && (after !== targetElement.lastChild)) {
targetElement.insertBefore(childElement, after.nextSibling);
} else {
targetElement.appendChild(childElement);
}
};
/**
* @private
* Given a string containing encoded entity references, returns the string with the entities decoded.
* @param {string} text The text to parse.
* @return {string} The decoded text.
*/
DOMEditHandler.prototype._parseEntities = function (text, forTextarea) {
// Kind of a hack: just set the innerHTML of a div to the text, which will parse the entities, then
// read the content out.
if (forTextarea) text = "<textarea>" + text + "</textarea>";
this.entityParseParent.innerHTML = text;
var result = forTextarea
? this.entityParseParent.firstChild.value
: this.entityParseParent.textContent;
this.entityParseParent.textContent = "";
return result;
};
/**
* @private
* @param {Node} node
* @return {boolean} true if node expects its content to be raw text (not parsed for entities) according to the HTML5 spec.
*/
function _isRawTextNode(node) {
return (node.nodeType === Node.ELEMENT_NODE && /script|style|noscript|noframes|noembed|iframe|xmp/i.test(node.tagName));
}
/**
* @private
* Replace a range of text and comment nodes with an optional new text node
* @param {Element} targetElement
* @param {Object} edit
*/
DOMEditHandler.prototype._textReplace = function (targetElement, edit) {
function prevIgnoringHighlights(node) {
do {
node = node.previousSibling;
} while (node && node.className === HIGHLIGHT_CLASSNAME);
return node;
}
function nextIgnoringHighlights(node) {
do {
node = node.nextSibling;
} while (node && node.className === HIGHLIGHT_CLASSNAME);
return node;
}
function lastChildIgnoringHighlights(node) {
node = (node.childNodes.length ? node.childNodes.item(node.childNodes.length - 1) : null);
if (node && node.className === HIGHLIGHT_CLASSNAME) {
node = prevIgnoringHighlights(node);
}
return node;
}
if (targetElement.tagName === "TEXTAREA")
return targetElement.value = this._parseEntities(edit.content, true);
var start = (edit.afterID) ? this._queryBracketsID(edit.afterID) : null,
startMissing = edit.afterID && !start,
end = (edit.beforeID) ? this._queryBracketsID(edit.beforeID) : null,
endMissing = edit.beforeID && !end,
moveNext = start && nextIgnoringHighlights(start),
current = moveNext || (end && prevIgnoringHighlights(end)) || lastChildIgnoringHighlights(targetElement),
next,
lastRemovedWasText,
isText,
textNode;
if (edit.content !== undefined)
textNode = this.htmlDocument.createTextNode(_isRawTextNode(targetElement)
? edit.content : this._parseEntities(edit.content));
// remove all nodes inside the range
while (current && (current !== end)) {
isText = current.nodeType === Node.TEXT_NODE;
// if start is defined, delete following text nodes
// if start is not defined, delete preceding text nodes
next = (moveNext) ? nextIgnoringHighlights(current) : prevIgnoringHighlights(current);
// only delete up to the nearest element.
// if the start/end tag was deleted in a prior edit, stop removing
// nodes when we hit adjacent text nodes
if ((current.nodeType === Node.ELEMENT_NODE) ||
((startMissing || endMissing) && (isText && lastRemovedWasText))) {
break;
} else {
lastRemovedWasText = isText;
current.remove();
current = next;
}
}
if (textNode) {
// OK to use nextSibling here (not nextIgnoringHighlights) because we do literally
// want to insert immediately after the start tag.
if (start && start.nextSibling) {
targetElement.insertBefore(textNode, start.nextSibling);
} else if (end) {
if (end.parentNode !== targetElement)
return console.error("missing tagID");
targetElement.insertBefore(textNode, end);
} else {
targetElement.appendChild(textNode);
}
}
};
/**
* @private
* Apply an array of DOM edits to the document
* @param {Array.<Object>} edits
*/
DOMEditHandler.prototype.apply = function (edits) {
var targetID,
targetElement,
childElement,
self = this;
this.rememberedNodes = {};
edits.forEach(function (edit) {
var editIsSpecialTag = edit.type === "elementInsert" && (edit.tag === "html" || edit.tag === "head" || edit.tag === "body");
if (edit.type === "rememberNodes") {
edit.tagIDs.forEach(function (tagID) {
var node = self._queryBracketsID(tagID);
if (node) {
self.rememberedNodes[tagID] = node;
node.remove();
}
});
return;
}
targetID = edit.type.match(/textReplace|textDelete|textInsert|elementInsert|elementMove/) ? edit.parentID : edit.tagID;
targetElement = self._queryBracketsID(targetID);
if (!targetElement && !editIsSpecialTag) {
console.error("data-cloud9-id=" + targetID + " not found");
return;
}
switch (edit.type) {
case "attrChange":
case "attrAdd":
targetElement.setAttribute(edit.attribute, self._parseEntities(edit.value));
break;
case "attrDelete":
targetElement.removeAttribute(edit.attribute);
break;
case "elementDelete":
targetElement.remove();
break;
case "elementInsert":
childElement = null;
if (editIsSpecialTag) {
// If we already have one of these elements (which we should), then
// just copy the attributes and set the ID.
childElement = self.htmlDocument[edit.tag === "html" ? "documentElement" : edit.tag];
if (!childElement) {
// Treat this as a normal insertion.
editIsSpecialTag = false;
}
}
if (!editIsSpecialTag) {
childElement = self.htmlDocument.createElement(edit.tag);
}
Object.keys(edit.attributes).forEach(function (attr) {
childElement.setAttribute(attr, self._parseEntities(edit.attributes[attr]));
});
childElement.setAttribute("data-cloud9-id", edit.tagID);
if (!editIsSpecialTag) {
self._insertChildNode(targetElement, childElement, edit);
}
break;
case "elementMove":
childElement = self._queryBracketsID(edit.tagID);
self._insertChildNode(targetElement, childElement, edit);
break;
case "textInsert":
var textElement = self.htmlDocument.createTextNode(_isRawTextNode(targetElement) ? edit.content : self._parseEntities(edit.content));
self._insertChildNode(targetElement, textElement, edit);
break;
case "textReplace":
case "textDelete":
self._textReplace(targetElement, edit);
break;
}
});
this.rememberedNodes = {};
// update highlight after applying diffs
redrawHighlights();
};
DOMEditHandler.prototype.initDom = function (docState) {
if (docState.idMap && !this.initialized) {
this.initialized = true;
var idMap = docState.idMap;
var walk = function (el) {
var id = el.getAttribute("data-cloud9-id");
if (idMap[id])
el.setAttribute("data-cloud9-id", idMap[id]);
var ch = el.children;
for (var i = 0; i < ch.length; i++) walk(ch[i]);
};
walk(document.documentElement);
}
else if (docState.dom) {
var dom = docState.dom;
if (!dom.children || document.documentElement.hasAttribute("data-cloud9-id"))
return;
var filter = function(dom) {
return dom.children = dom.children && dom.children.filter(function(x) {
if (x.children)
return filter(x);
});
};
filter(dom);
var init = function(htmlNode, dom) {
htmlNode.setAttribute("data-cloud9-id", dom.tagID);
var children = htmlNode.children;
var domChildren = dom.children;
var match = false;
for (var i = 0, j = 0; i < children.length; i++) {
var htmlCh = children[i];
var domCh = domChildren[j];
j++;
if (!domCh || domCh.tag !== htmlCh.tagName.toLowerCase())
continue;
match = true;
init(children[i], domChildren[i]);
}
if (domChildren.length != children.length) {
if (!match && children.length == 1)
init(children[0], dom);
}
};
document.documentElement.setAttribute("data-cloud9-id", 1);
var head = document.head;
var body = document.body;
head.setAttribute("data-cloud9-id", 2);
body.setAttribute("data-cloud9-id", 3);
var root = {
children: [],
setAttribute: function() {}
};
for (var ch = head.children, i = 0; i < ch.length; i++) {
root.children.push(ch[i]);
}
for (var ch = body.children, i = 0; i < ch.length; i++) {
root.children.push(ch[i]);
}
var flatten = function(node, i) {
var args = [i, 1].concat(node.children[i].children);
node.children.splice.apply(node.children, args);
};
if (dom.tag == "html") {
for (var ch = dom.children, i = ch.length; i--; ) {
if (ch[i].tag == "head" || ch[i].tag == "body")
flatten(dom, i);
}
}
init(root, dom);
}
if (docState.edits)
this.apply(docState.edits);
};
/**
*
* @param {Element} elem
*/
function _domElementToJSON(elem) {
var json = { tag: elem.tagName.toLowerCase(), attributes: {}, children: [] },
i,
len,
node,
value;
len = elem.attributes.length;
for (i = 0; i < len; i++) {
node = elem.attributes.item(i);
value = (node.name === "data-cloud9-id") ? parseInt(node.value, 10) : node.value;
json.attributes[node.name] = value;
}
len = elem.childNodes.length;
for (i = 0; i < len; i++) {
node = elem.childNodes.item(i);
// ignores comment nodes and visuals generated by live preview
if (node.nodeType === Node.ELEMENT_NODE && node.className !== HIGHLIGHT_CLASSNAME) {
json.children.push(_domElementToJSON(node));
} else if (node.nodeType === Node.TEXT_NODE) {
json.children.push({ content: node.nodeValue });
}
}
return json;
}
function getSimpleDOM() {
return JSON.stringify(_domElementToJSON(document.documentElement));
}
// init
var editHandler = new DOMEditHandler(window.document);
// Update Style Rule
function findCssRule(name, stylesheet) {
// chrome normalizes pseudo-elements to :: and firefox to :
name = name.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
.replace(/::?(after|before)/g, "::?$1");
var nameRe = new RegExp("^" + name + "$", "i");
var rules, i;
if (!stylesheet) {
var sheets = window.document.styleSheets;
for (var j = sheets.length - 1; j >= 0; j--) {
try {
rules = sheets[j].cssRules;
for (i = 0; i < rules.length; i++) {
if (nameRe.test(rules.item(i).selectorText)) {
return rules.item(i);
}
}
}
catch (e) {}
}
}
else {
if (typeof stylesheet == "number")
stylesheet = window.document.styleSheets[stylesheet || 0];
rules = stylesheet.cssRules;
if (!rules) return false;
for (i = 0; i < rules.length; i++) {
if (nameRe.test(rules.item(i).selectorText)) {
return rules.item(i);
}
}
}
}
/**
* This method sets a single CSS rule.
* @param {String} name The CSS name of the rule (i.e. `.cls` or `#id`).
* @param {String} type The CSS property to change.
* @param {String} value The CSS value of the property.
* @param {String} [stylesheet] The name of the stylesheet to change.
* @param {Object} [win] A reference to a window
*/
function setStyleRule(name, type, value, stylesheet, win) {
var rule = findCssRule(name, stylesheet, win);
if (rule) {
if (value.indexOf("!important") > -1) {
rule.style.cssText = type + ":" + value;
} else {
type = type.replace(/-(\w)/g, function(_, a) {return a.toUpperCase()});
rule.style[type] = value;
}
}
return !!rule;
}
function updateStyleRule(url, rule) {
var link = reloader.findStylesheet(url);
if (!link) return;
setStyleRule(rule.selector, rule.key, rule.value,
link.object.rule ? link.object.rule.styleSheet : link.object.node.sheet);
}
function send(message) {
if (!session) return;
message.id = session.id;
parent.postMessage(message,
host == "local" ? "*" : host);
}
window.start = function(win) {
parent = win;
init(id);
};
// Make sure everything is loaded
if (parent != window)
init(id);
})();