define(function(require, exports, module) { "use strict"; var CHAR_HEIGHT = 10; var CHAR_WIDTH = 6; var WINDOW_HEIGHT = 768; var WINDOW_WIDTH = 1024; function Style() { } Style.prototype.__defineGetter__("cssText", function() { var cssText = ""; Object.keys(this).forEach(function(key) { if (this[key]) cssText += (cssText ? " " : "") + key + ": " + this[key] + ";"; }, this); return cssText; }); Style.prototype.__defineSetter__("cssText", function(value) { Object.keys(this).forEach(function(key) { delete this[key]; }, this); value.split(";").forEach(function(key) { var parts = key.split(":"); if (parts.length == 2) this[parts[0].trim()] = parts[1].trim(); }, this); }); var initializers = { textarea: function() { this.setSelectionRange = function(start, end) { this.selectionStart = Math.min(start, this.value.length, end); this.selectionEnd = Math.min(end, this.value.length); }; this.value = ""; } }; function Node(name) { this.localName = name; this.value = ""; this.tagName = name && name.toUpperCase(); this.children = this.childNodes = []; this.ownerDocument = window.document || this; this.$attributes = {}; this.style = new Style(); if (initializers.hasOwnProperty(name)) initializers[name].call(this); } (function() { this.cloneNode = function(recursive) { var clone = new Node(this.localName); for (var i in this.$attributes) { clone.setAttribute(i, this.$attributes[i]); } if (recursive) { this.children.forEach(function(ch) { clone.appendChild(ch.cloneNode(true)); }, this); } return clone; }; this.appendChild = function(node) { return this.insertBefore(node, null); }; this.removeChild = function(node) { var i = this.children.indexOf(node); if (i == -1) throw new Error("not a child"); node.parentNode = null; this.children.splice(i, 1); if (!document.contains(document.activeElement)) document.activeElement = document.body; }; this.remove = function() { this.parentNode && this.parentNode.removeChild(this); }; this.replaceChild = function(node, oldNode) { this.insertBefore(node, oldNode); this.removeChild(oldNode); return oldNode; }; this.insertBefore = function(node, before) { if (node.parentNode) node.parentNode.removeChild(node); var i = this.children.indexOf(before); if (i == -1) i = this.children.length + 1; if (node.localName == "#fragment") { var children = node.children.slice(); for (var j = 0; j < children.length; j++) this.insertBefore(children[j], before); } else { this.children.splice(i - 1, 0, node); node.parentNode = this; } return node; }; this.querySelectorAll = function(s) { var nodes = []; walk(this, function(node) { if (node.localName == s) nodes.push(node); }); return nodes; }; this.querySelector = function(s) { return this.querySelectorAll(s)[0]; }; this.getElementsByTagName = function(s) { var nodes = []; walk(this, function(node) { if (node.localName == s) nodes.push(node); }); return nodes; }; this.getElementById = function(s) { return walk(this, function(node) { // console.log(node.getAttribute && node.getAttribute("id")) if (node.getAttribute && node.getAttribute("id") == s) return node; }); }; this.setAttribute = function(a, v) { this.$attributes[a] = v; }; this.getAttribute = function(a, v) { return String(this.$attributes[a]); }; this.__defineGetter__("attributes", function() { var style = this.style.cssText; if (style) this.$attributes.style = style; return Object.keys(this.$attributes).map(function(key) { return {name: key, value: this.$attributes[key]}; }, this); }); this.__defineGetter__("className", function() { return this.$attributes.class || ""; }); this.__defineSetter__("className", function(v) { this.$attributes.class = v; }); this.__defineGetter__("textContent", function() { var v = ""; walk(this, function(node) { if (node instanceof TextNode) v += node.data; }); return v; }); this.__defineSetter__("textContent", function(v) { removeAllChildren(this); this.appendChild(new TextNode(v)); }); this.__defineGetter__("id", function() { return this.getAttribute("id"); }); this.__defineSetter__("id", function(v) { this.setAttribute("id", v); }); this.__defineGetter__("parentElement", function() { return this.parentNode == document ? null : this.parentNode; }); this.__defineGetter__("innerHTML", function() { return this.children.map(function(ch) { return "outerHTML" in ch ? ch.outerHTML : ch.data; }).join(""); }); this.__defineGetter__("outerHTML", function() { var attributes = this.attributes.map(function(attr) { return attr.name + "=" + JSON.stringify(attr.value); }, this).join(" "); return "<" + this.localName + (attributes ? " " + attributes : "") + ">" + this.innerHTML + ""; }); this.__defineSetter__("innerHTML", function(v) { removeAllChildren(this); var root = this; var tagRe = /<(\/?\w+)|&(?:(#?\w+);)|$/g; var skipped = ""; for (var m, lastIndex = 0; m = tagRe.exec(v);) { skipped += v.substring(lastIndex, m.index); if (m[2]) { if (m[2] == "gt") { skipped += ">"; } else if (m[2] == "lt") { skipped += "<"; } else if (m[2] == "amp") { skipped += "&"; } lastIndex = tagRe.lastIndex ; } else { if (skipped) { root.appendChild(document.createTextNode(skipped)); skipped = ""; } var end = v.indexOf(">", tagRe.lastIndex); var attributes = v.slice(tagRe.lastIndex, end).trim(); tagRe.lastIndex = lastIndex = end < 0 ? v.length : end + 1; if (!m[1]) { return; } if (m[1][0] == "/") { if (root != this) root = root.parentNode; } else { var tagName = m[1]; root = root.appendChild(document.createElement(tagName)); attributes && attributes.replace(/([\w:-]+)\s*=\s*(?:"([^"]*)"|'([^"]*)'|(\w+))/, function(_, key, v1,v2,v3) { root.setAttribute(key, v1 || v2 || v3 || key); }); } } } }); this.getBoundingClientRect = function(v) { var w = 0; var h = 0; var top = 0; var left = 0; if (this == document.documentElement) { w = WINDOW_WIDTH; h = WINDOW_HEIGHT; } else if (!document.contains(this)) { w = h = 0; } else if (this.style.width == "auto" || this.localName == "span") { w = this.textContent.length * CHAR_WIDTH; h = CHAR_HEIGHT; } else if (this.style.width) { w = parseFloat(this.style.width) || 0; h = parseFloat(this.style.height) || 0; top = parseFloat(this.style.top) || 0; left = parseFloat(this.style.left) || 0; } else if (this.style.right) { var rect = this.parentNode.getBoundingClientRect(); w = rect.width - (parseFloat(this.style.left) || 0) - (parseFloat(this.style.right) || 0); h = parseFloat(this.style.height) || rect.height; top = rect.top; left = rect.left + (parseFloat(this.style.left) || 0); } else if (this.localName == "div") { w = WINDOW_WIDTH; } return {top: top, left: left, width: w, height: h, right: 0}; }; this.__defineGetter__("clientHeight", function() { return this.getBoundingClientRect().height; }); this.__defineGetter__("clientWidth", function() { return this.getBoundingClientRect().width; }); this.__defineGetter__("offsetHeight", function() { return this.getBoundingClientRect().height; }); this.__defineGetter__("offsetWidth", function() { return this.getBoundingClientRect().width; }); this.__defineGetter__("lastChild", function() { return this.childNodes[this.childNodes.length - 1]; }); this.__defineGetter__("firstChild", function() { return this.childNodes[0]; }); // TODO this is a waorkaround for scrollHeight usage in virtualRenderer this.scrollHeight = 1; this.addEventListener = function(name, listener, capturing) { if (!listener) { console.trace(); throw new Error("attempting to add empty listener"); } if (!this._events) this._events = {}; if (!this._events[name]) this._events[name] = []; var i = this._events[name].indexOf(listener); if (i == -1) this._events[name][capturing ? "unshift" : "push"](listener); }; this.removeEventListener = function(name, listener) { if (!this._events) return; if (!this._events[name]) return; var i = this._events[name].indexOf(this._events[name]); if (i !== -1) this._events[name].splice(i, 1); }; this.createEvent = function(v) { return new Event(); }; this.dispatchEvent = function(e) { if (!e.target) e.target = this; if (!e.timeStamp) e.timeStamp = Date.now(); e.currentTarget = this; var events = this._events && this._events[e.type]; events && events.forEach(function(listener) { if (!e.stopped) listener.call(this, e); }, this); if (!e.bubbles) return; if (this.parentNode) this.parentNode.dispatchEvent(e); else if (this != window) window.dispatchEvent(e); }; this.contains = function(node) { while (node) { if (node == this) return true; node = node.parentNode; } }; this.focus = function() { if (document.activeElement == this) return; if (document.activeElement) document.activeElement.dispatchEvent({type: "blur"}); document.activeElement = this; this.dispatchEvent({type: "focus"}); }; this.blur = function() { document.body.focus(); }; function removeAllChildren(node) { node.children.forEach(function(node) { node.parentNode = null; }); node.children.length = 0; if (!document.contains(document.activeElement)) document.activeElement = document.body; } }).call(Node.prototype); function Event(type) { this.type = type; } (function() { this.initMouseEvent = function( type, _1, _2, window, detail, x, y, _x, _y, ctrl, alt, shift, meta, button, relatedTarget ) { this.bubbles = true; this.type = type; this.detail = detail || 0; this.clientX = x; this.clientY = y; this.button = button; this.relatedTarget = relatedTarget; this.ctrlKey = ctrl; this.altKey = alt; this.shiftKey = shift; this.metaKey = meta; }; this.preventDefault = function() { this.defaultPrevented = true; }; this.stopPropagation = function() { this.stopped = true; }; }).call(Event.prototype); function walk(node, fn) { var ch = node.children || []; for (var i = 0; i < ch.length; i++) { var result = fn(ch[i]) || walk(ch[i], fn); if (result) return result; } } function TextNode(value) { this.data = value || ""; } TextNode.prototype.cloneNode = function() { return new TextNode(this.data); }; var window = {}; var document = new Node(); document.navigator = {}; document.createElementNS = function(ns, t) { return new Node(t); }; document.createElement = function(t) { return new Node(t); }; document.createTextNode = function(v) { return new TextNode(v); }; document.createDocumentFragment = function() { return new Node("#fragment"); }; document.hasFocus = function() { return true; }; document.documentElement = document.appendChild(new Node("html")); document.body = new Node("body"); document.head = new Node("head"); document.documentElement.appendChild(document.head); document.documentElement.appendChild(document.body); window.document = document; window.document.defaultView = window; window.setTimeout = setTimeout; window.clearTimeout = clearTimeout; window.getComputedStyle = function(node) { return node.style; }; window.addEventListener = document.addEventListener; window.removeEventListener = document.removeEventListener; window.dispatchEvent = document.dispatchEvent; window.name = "nodejs"; window.focus = function() {}; window.Node = window.Element = Node; window.window = window; window.CustomEvent = window.Event = Event; window.requestAnimationFrame = function(callback) { return setTimeout(function() { callback(); }); }; var addedProperties = []; exports.load = function() { if (typeof global == "undefined") return; Object.keys(window).forEach(function(i) { if (!global[i]) { addedProperties.push(i); global[i] = window[i]; } }); }; exports.loadInBrowser = function(global) { delete global.ResizeObserver; Object.keys(window).forEach(function(i) { if (i != "document" && i != "window") { delete global[i]; global[i] = window[i]; } }); for (var i in window.document) { var val = window.document[i]; if (typeof val == "function") { if (i == "createElement") { global.document.createElementOrig = global.document.createElement; val = function(n) { if (n == "script") return global.document.createElementOrig(n); return window.document.createElement(n); }; } val = val.bind(window.document); } Object.defineProperty( global.document, i, { value: val, enumerable: true, configurable: true } ); } }; exports.unload = function() { if (typeof global == "undefined") return; var req = require; var cache = req("module")._cache; delete cache[__filename]; addedProperties.forEach(function(i) { delete global[i]; }); }; exports.load(); });