From 8c26c4e16b4aa273977a1e4fa5895d95980d5bed Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 29 Jun 2017 16:02:53 +0400 Subject: [PATCH 1/4] remove unnecessary uses of innerHTML --- plugins/c9.ide.ace.split/split.js | 4 +- plugins/c9.ide.collab/chat/chat.js | 2 +- plugins/c9.ide.collab/cursor_layer.js | 2 +- .../notifications/notifications.js | 2 +- plugins/c9.ide.collab/share/share.js | 10 +- .../c9.ide.collab/timeslider/timeslider.js | 8 +- plugins/c9.ide.dialog.login/login.js | 2 +- plugins/c9.ide.editors/document.js | 2 +- plugins/c9.ide.editors/timeview.js | 2 +- plugins/c9.ide.find.replace/libsearch.js | 2 +- plugins/c9.ide.help/help.js | 3 +- plugins/c9.ide.imgeditor/imgeditor.js | 1 - .../c9.ide.immediate/evaluators/debugnode.js | 2 +- plugins/c9.ide.installer/gui.js | 2 +- .../c9.ide.server/views/flat-load-screen.html | 2 +- plugins/c9.ide.test/coverage.js | 2 +- plugins/c9.ide.ui/lib_apf.js | 318 +----------------- plugins/c9.vfs.standalone/www/ide.html | 2 +- 18 files changed, 41 insertions(+), 327 deletions(-) diff --git a/plugins/c9.ide.ace.split/split.js b/plugins/c9.ide.ace.split/split.js index 1e749ad0..631b6382 100644 --- a/plugins/c9.ide.ace.split/split.js +++ b/plugins/c9.ide.ace.split/split.js @@ -160,9 +160,9 @@ define(function(require, exports, module) { function createGrabber(editor) { var htmlNode = editor.ace.container.parentNode; var grabber = document.createElement("div"); - htmlNode.appendChild(grabber); grabber.className = "splitgrabber"; - grabber.innerHTML = "="; + grabber.textContent = "="; + htmlNode.appendChild(grabber); grabber.addEventListener("mousedown", function(e) { startSplit(e, grabber, editor); diff --git a/plugins/c9.ide.collab/chat/chat.js b/plugins/c9.ide.collab/chat/chat.js index 5089e8ce..f416a50a 100644 --- a/plugins/c9.ide.collab/chat/chat.js +++ b/plugins/c9.ide.collab/chat/chat.js @@ -100,7 +100,7 @@ define(function(require, exports, module) { var chatHistory = workspace.chatHistory || []; chatHistory.forEach(addMessage); scrollDown(); - chatCounter.innerHTML = chatHistory.length; + chatCounter.textContent = chatHistory.length; } chatInput.ace.commands.addCommands([ diff --git a/plugins/c9.ide.collab/cursor_layer.js b/plugins/c9.ide.collab/cursor_layer.js index 11b96ff9..280c6047 100644 --- a/plugins/c9.ide.collab/cursor_layer.js +++ b/plugins/c9.ide.collab/cursor_layer.js @@ -258,7 +258,7 @@ define(function(require, module, exports) { document.body.appendChild(node); node.className = "cool_tooltip_cursor"; - node.innerHTML = "" + apf.htmlentities(fullname) + ""; + node.innerHTML = "" + util.escapeHTML(fullname) + ""; // create the arrow var arrow = document.createElement("div"); diff --git a/plugins/c9.ide.collab/notifications/notifications.js b/plugins/c9.ide.collab/notifications/notifications.js index 59adcaf3..746912ee 100644 --- a/plugins/c9.ide.collab/notifications/notifications.js +++ b/plugins/c9.ide.collab/notifications/notifications.js @@ -174,7 +174,7 @@ define(function(require, exports, module) { if (bubble) { if (count) { - bubble.innerHTML = count; + bubble.textContent = count; bubble.style.display = "block"; bubble.className = "newnotifs size" + String(count).length; } else { diff --git a/plugins/c9.ide.collab/share/share.js b/plugins/c9.ide.collab/share/share.js index f6a7c7fb..5dfc04c6 100644 --- a/plugins/c9.ide.collab/share/share.js +++ b/plugins/c9.ide.collab/share/share.js @@ -130,20 +130,20 @@ define(function(require, module, exports) { var port = (options.local ? ":" + (c9.port || "8080") : ""); if (!options.local) { var l = location; - shareLinkEditor.innerHTML = l.protocol + "//" + l.host + l.pathname; + shareLinkEditor.textContent = l.protocol + "//" + l.host + l.pathname; } else { - shareLinkEditor.innerHTML = "https://ide.c9.io/" + c9.workspaceId; + shareLinkEditor.textContent = "https://ide.c9.io/" + c9.workspaceId; } - shareLinkApp.innerHTML = (c9.hostname + shareLinkApp.textContent = (c9.hostname ? "https://" + c9.hostname : "http://localhost") + port; - shareLinkPreview.innerHTML = options.previewUrl; + shareLinkPreview.textContent = options.previewUrl; [shareLinkEditor, shareLinkApp, shareLinkPreview].forEach(function(div) { div.addEventListener("click", function(e) { - mnuLink.meta.linkText = this.innerHTML; + mnuLink.meta.linkText = this.textContent; mnuLink.show(e.clientX + 1, e.clientY); }); div.addEventListener("contextmenu", function(e) { diff --git a/plugins/c9.ide.collab/timeslider/timeslider.js b/plugins/c9.ide.collab/timeslider/timeslider.js index 17726043..9d982c7d 100644 --- a/plugins/c9.ide.collab/timeslider/timeslider.js +++ b/plugins/c9.ide.collab/timeslider/timeslider.js @@ -367,7 +367,7 @@ define(function(require, exports, module) { sliderProgress.style.transition = "none"; var newSliderPos = calcSliderPos(evt2.clientX); - revisionLabel.innerHTML = "Version " + newSliderPos; + revisionLabel.textContent = "Version " + newSliderPos; setHandleLeft(clamp(evt2.clientX)); if (sliderPos != newSliderPos) { sliderPos = newSliderPos; @@ -476,7 +476,7 @@ define(function(require, exports, module) { } function updateTimer(time) { - revisionDate.innerHTML = dateFormat(time); + revisionDate.textContent = dateFormat(time); } function calcHandlerLeft(pos) { @@ -492,11 +492,11 @@ define(function(require, exports, module) { setHandleLeft(calcHandlerLeft(newpos)); if (savedRevisionNums.indexOf(newpos) === -1) { - revisionLabel.innerHTML = "Version " + newpos; + revisionLabel.textContent = "Version " + newpos; revisionLabel.className = "revision_label"; } else { - revisionLabel.innerHTML = "Saved Version " + newpos; + revisionLabel.textContent = "Saved Version " + newpos; revisionLabel.className = "revision_label saved"; } diff --git a/plugins/c9.ide.dialog.login/login.js b/plugins/c9.ide.dialog.login/login.js index 225760f0..534f1b9d 100644 --- a/plugins/c9.ide.dialog.login/login.js +++ b/plugins/c9.ide.dialog.login/login.js @@ -51,7 +51,7 @@ define(function(require, module, exports) { function show(title, description, onChoose, onCancel, options) { draw(); - lblExplanation.$ext.innerHTML = util.escapeXml(description); + lblExplanation.$ext.textContent = description; plugin.title = title || "Please Sign In"; var chosen; diff --git a/plugins/c9.ide.editors/document.js b/plugins/c9.ide.editors/document.js index 47699bc6..5ccd1a8d 100644 --- a/plugins/c9.ide.editors/document.js +++ b/plugins/c9.ide.editors/document.js @@ -454,7 +454,7 @@ define(function(require, module, exports) { * Example: * * doc.on("setValue", function(e) { - * myDiv.innerHTML = e.value + * myDiv.textContent = e.value * }, doc.getSession()); * * @event setValue diff --git a/plugins/c9.ide.editors/timeview.js b/plugins/c9.ide.editors/timeview.js index 3e077168..ffd3fa88 100644 --- a/plugins/c9.ide.editors/timeview.js +++ b/plugins/c9.ide.editors/timeview.js @@ -66,7 +66,7 @@ define(function(require, exports, module) { + dt.getSeconds(); if (doc.tab.isActive()) - editor.innerHTML = time; + editor.textContent = time; doc.title = time; }, 1); }); diff --git a/plugins/c9.ide.find.replace/libsearch.js b/plugins/c9.ide.find.replace/libsearch.js index e2fd0cd8..ba6f99f1 100644 --- a/plugins/c9.ide.find.replace/libsearch.js +++ b/plugins/c9.ide.find.replace/libsearch.js @@ -134,7 +134,7 @@ module.exports = function(settings, execFind, toggleDialog, restore, toggleOptio new RegExp(searchTxt); } catch (e) { - tooltip.$ext.innerHTML = apf.escapeXML(e.message.replace(": /" + searchTxt + "/", "")); + tooltip.$ext.textContent = e.message.replace(": /" + searchTxt + "/", ""); tooltip.$ext.style.opacity = 1; var pos = apf.getAbsolutePosition(win.$ext); diff --git a/plugins/c9.ide.help/help.js b/plugins/c9.ide.help/help.js index 7fa537db..22420ec3 100644 --- a/plugins/c9.ide.help/help.js +++ b/plugins/c9.ide.help/help.js @@ -133,8 +133,7 @@ define(function(require, exports, module) { aboutDialog.show(); // shorten commit hash in c9.version var version = c9.version.replace(/([a-f\d]{10})[a-f\d]{30}/, "$1"); - document.getElementById("c9Version").innerHTML - = ui.escapeXML("Version " + version); + document.getElementById("c9Version").textContent = "Version " + version; } /***** Lifecycle *****/ diff --git a/plugins/c9.ide.imgeditor/imgeditor.js b/plugins/c9.ide.imgeditor/imgeditor.js index bf6a4645..15d96fe9 100644 --- a/plugins/c9.ide.imgeditor/imgeditor.js +++ b/plugins/c9.ide.imgeditor/imgeditor.js @@ -305,7 +305,6 @@ define(function(require, exports, module) { ? path : vfs.url(path); - // editor.setProperty("value", apf.escapeXML(fullpath)); loadCanvas(doc.tab, fullpath, callback); } diff --git a/plugins/c9.ide.immediate/evaluators/debugnode.js b/plugins/c9.ide.immediate/evaluators/debugnode.js index 6aabc4db..fd3a5d3f 100644 --- a/plugins/c9.ide.immediate/evaluators/debugnode.js +++ b/plugins/c9.ide.immediate/evaluators/debugnode.js @@ -464,7 +464,7 @@ define(function(require, exports, module) { lastCell = cell; if (cell.html) - cell.html.innerHTML = ""; + cell.html.textContent = ""; evaluateHeadless(expression, function(result) { if (cell.setError && result["$$error"]) diff --git a/plugins/c9.ide.installer/gui.js b/plugins/c9.ide.installer/gui.js index 7daf9589..8932c150 100644 --- a/plugins/c9.ide.installer/gui.js +++ b/plugins/c9.ide.installer/gui.js @@ -329,7 +329,7 @@ define(function(require, exports, module) { }); plugin.addOther(function() { - div.innerHTML = ""; + div.textContent = ""; div.parentNode.removeChild(div); terminal.destroy(); diff --git a/plugins/c9.ide.server/views/flat-load-screen.html b/plugins/c9.ide.server/views/flat-load-screen.html index a8263ad4..e8aba054 100644 --- a/plugins/c9.ide.server/views/flat-load-screen.html +++ b/plugins/c9.ide.server/views/flat-load-screen.html @@ -76,7 +76,7 @@ location.hash = location.hash.replace(/&?create/, ""); } - document.querySelector("#loadingide .cool-message").innerHTML = msg; + document.querySelector("#loadingide .cool-message").textContent = msg; setTimeout(function(){ var s = document.querySelector("#loadingide .status"); diff --git a/plugins/c9.ide.test/coverage.js b/plugins/c9.ide.test/coverage.js index 45a4ddf3..d7298eff 100644 --- a/plugins/c9.ide.test/coverage.js +++ b/plugins/c9.ide.test/coverage.js @@ -155,7 +155,7 @@ define(function(require, exports, module) { draw(); var amount = button.$ext.querySelector(".amount"); - amount.innerHTML = totalCoverage + "%"; + amount.textContent = totalCoverage + "%"; button.show(); } diff --git a/plugins/c9.ide.ui/lib_apf.js b/plugins/c9.ide.ui/lib_apf.js index f22b499b..d69fd99a 100644 --- a/plugins/c9.ide.ui/lib_apf.js +++ b/plugins/c9.ide.ui/lib_apf.js @@ -460,9 +460,6 @@ define(["require", "module", "exports", "./lib/menu/menu", "./lib/crypto", return null; }, - html_entity_decode: function(s){return s}, - htmlentities: function(s){return s}, - /** * Formats an Ajax.org Platform error message. * @param {Number} number The number of the error. This can be used to look up more information about the error. @@ -1733,111 +1730,6 @@ if (!String.prototype.repeat) { return Array(times + 1).join(this); }; } -/* - * Count the number of occurences of substring 'str' inside a string - * - * @param {String} str - * @type {Number} - */ -String.prototype.count = function(str) { - return this.split(str).length - 1; -}; - -/* - * Remove HTML or any XML-like tags from a string - * - * @type {String} - */ -String.prototype.stripTags = function() { - return this.replace(/<\/?[^>]+>/gi, ""); -}; - -/* - * Wrapper for the global 'escape' function for strings - * - * @type {String} - */ -String.prototype.escape = function() { - return escape(this); -}; - -/* - * Returns an xml document - * @type {XMLElement} - */ -String.prototype.toXml = function(){ - var node = apf.getXml("" + this + ""); - if (node.childNodes.length == 1) { - return node.childNodes[0]; - } - else { - var docFrag = node.ownerDocument.createDocumentFragment(), - nodes = node.childNodes; - while (nodes.length) - docFrag.appendChild(nodes[0]); - return docFrag; - } -}; - - -if (typeof window != "undefined" && typeof window.document != "undefined" - && typeof window.document.createElement == "function") { - /* - * Encode HTML entities to its HTML equivalents, like '&' to '&amp;' - * and '<' to '&lt;'. - * - * @type {String} - * @todo is this fast? - */ - String.prototype.escapeHTML = function() { - this.escapeHTML.text.data = this; - return this.escapeHTML.div.innerHTML; - }; - - /* - * Decode HTML equivalent entities to characters, like '&amp;' to '&' - * and '&lt;' to '<'. - * - * @type {String} - */ - String.prototype.unescapeHTML = function() { - var div = document.createElement("div"); - div.innerHTML = this.stripTags(); - if (div.childNodes[0]) { - if (div.childNodes.length > 1) { - var out = []; - for (var i = 0; i < div.childNodes.length; i++) - out.push(div.childNodes[i].nodeValue); - return out.join(""); - } - else - return div.childNodes[0].nodeValue; - } - return ""; - }; - - String.prototype.escapeHTML.div = document.createElement("div"); - String.prototype.escapeHTML.text = document.createTextNode(""); - String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text); - - if ("<\n>".escapeHTML() !== "<\n>") - String.prototype.escapeHTML = null; - - if ("<\n>".unescapeHTML() !== "<\n>") - String.prototype.unescapeHTML = null; -} - -if (!String.prototype.escapeHTML) { - String.prototype.escapeHTML = function() { - return this.replace(/&/g,"&").replace(//g,">"); - }; -} - -if (!String.prototype.unescapeHTML) { - String.prototype.unescapeHTML = function() { - return this.stripTags().replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&"); - }; -} /* * Trim a string down to a specific number of characters. Optionally, append an @@ -1885,34 +1777,6 @@ String.prototype.splitSafe = function(separator, limit, bLowerCase) { .split(new RegExp("[\\s ]*" + separator + "[\\s ]*", "g"), limit || 999); }; -/* - * Appends a random number with a specified length to this String instance. - * - * @see randomGenerator - * @param {Number} length - * @type {String} - */ -String.prototype.appendRandomNumber = function(length) { - for (var arr = [], i = 1; i <= length; i++) - arr.push(apf.randomGenerator.generate(1, 9)); - // Create a new string from the old one, don't just create a copy - return this.toString() + arr.join(""); -}; - -/* - * Prepends a random number with a specified length to this String instance. - * - * @see randomGenerator - * @param {Number} length - * @type {String} - */ -String.prototype.prependRandomNumber = function(length) { - for (var arr = [], i = 1; i <= length; i++) - arr.push(apf.randomGenerator.generate(1, 9)); - // Create a new string from the old one, don't just create a copy - return arr.join("") + this.toString(); -}; - /* * Returns a string produced according to the formatting string. It replaces * all %s occurrences with the arguments provided. @@ -2870,43 +2734,6 @@ apf.getHtmlInnerHeight = function(oHtml) { - (parseInt(apf.getStyle(oHtml, "borderBottomWidth")) || 0)); }; - - - - -/** - * Escapes HTML from a string. - * @param {String} str The html to be escaped. - * @return {String} The escaped string. - */ -apf.htmlentities = function(str) { - return str.escapeHTML(); -}; - -/** - * Escape an XML string, making it ascii compatible. - * @param {String} str The xml string to escape. - * @return {String} The escaped string. - * @method xmlentities - * - */ - /* @todo This function does something completely different from htmlentities, - * the name is confusing and misleading. - */ -apf.xmlentities = apf.escapeXML; - -/** - * Unescape an HTML string. - * @param {String} str The string to unescape. - * @return {String} The unescaped string. - */ -apf.html_entity_decode = function(str) { - return (str || "").replace(/\&\#38;/g, "&").replace(/</g, "<") - .replace(/>/g, ">").replace(/&/g, "&").replace(/ /g, " "); -}; - - - /** * Determines whether the keyboard input was a character that can influence * the value of an element (like a textbox). @@ -3233,125 +3060,18 @@ apf.isChildOf = function(pNode, childnode, orItself) { return false; }; -apf.xmlEntityMap = { - "quot": "34", "amp": "38", "apos": "39", "lt": "60", "gt": "62", - "nbsp": "160", "iexcl": "161", "cent": "162", "pound": "163", "curren": "164", - "yen": "165", "brvbar": "166", "sect": "167", "uml": "168", "copy": "169", - "ordf": "170", "laquo": "171", "not": "172", "shy": "173", "reg": "174", - "macr": "175", "deg": "176", "plusmn": "177", "sup2": "178", "sup3": "179", - "acute": "180", "micro": "181", "para": "182", "middot": "183", "cedil": "184", - "sup1": "185", "ordm": "186", "raquo": "187", "frac14": "188", "frac12": "189", - "frac34": "190", "iquest": "191", "agrave": ["192", "224"], "aacute": ["193", "225"], - "acirc": ["194", "226"], "atilde": ["195", "227"], "auml": ["196", "228"], - "aring": ["197", "229"], "aelig": ["198", "230"], "ccedil": ["199", "231"], - "egrave": ["200", "232"], "eacute": ["201", "233"], "ecirc": ["202", "234"], - "euml": ["203", "235"], "igrave": ["204", "236"], "iacute": ["205", "237"], - "icirc": ["206", "238"], "iuml": ["207", "239"], "eth": ["208", "240"], - "ntilde": ["209", "241"], "ograve": ["210", "242"], "oacute": ["211", "243"], - "ocirc": ["212", "244"], "otilde": ["213", "245"], "ouml": ["214", "246"], - "times": "215", "oslash": ["216", "248"], "ugrave": ["217", "249"], - "uacute": ["218", "250"], "ucirc": ["219", "251"], "uuml": ["220", "252"], - "yacute": ["221", "253"], "thorn": ["222", "254"], "szlig": "223", "divide": "247", - "yuml": ["255", "376"], "oelig": ["338", "339"], "scaron": ["352", "353"], - "fnof": "402", "circ": "710", "tilde": "732", "alpha": ["913", "945"], - "beta": ["914", "946"], "gamma": ["915", "947"], "delta": ["916", "948"], - "epsilon": ["917", "949"], "zeta": ["918", "950"], "eta": ["919", "951"], - "theta": ["920", "952"], "iota": ["921", "953"], "kappa": ["922", "954"], - "lambda": ["923", "955"], "mu": ["924", "956"], "nu": ["925", "957"], - "xi": ["926", "958"], "omicron": ["927", "959"], "pi": ["928", "960"], - "rho": ["929", "961"], "sigma": ["931", "963"], "tau": ["932", "964"], - "upsilon": ["933", "965"], "phi": ["934", "966"], "chi": ["935", "967"], - "psi": ["936", "968"], "omega": ["937", "969"], "sigmaf": "962", "thetasym": "977", - "upsih": "978", "piv": "982", "ensp": "8194", "emsp": "8195", "thinsp": "8201", - "zwnj": "8204", "zwj": "8205", "lrm": "8206", "rlm": "8207", "ndash": "8211", - "mdash": "8212", "lsquo": "8216", "rsquo": "8217", "sbquo": "8218", "ldquo": "8220", - "rdquo": "8221", "bdquo": "8222", "dagger": ["8224", "8225"], "bull": "8226", - "hellip": "8230", "permil": "8240", "prime": ["8242", "8243"], "lsaquo": "8249", - "rsaquo": "8250", "oline": "8254", "frasl": "8260", "euro": "8364", - "image": "8465", "weierp": "8472", "real": "8476", "trade": "8482", - "alefsym": "8501", "larr": ["8592", "8656"], "uarr": ["8593", "8657"], - "rarr": ["8594", "8658"], "darr": ["8595", "8659"], "harr": ["8596", "8660"], - "crarr": "8629", "forall": "8704", "part": "8706", "exist": "8707", "empty": "8709", - "nabla": "8711", "isin": "8712", "notin": "8713", "ni": "8715", "prod": "8719", - "sum": "8721", "minus": "8722", "lowast": "8727", "radic": "8730", "prop": "8733", - "infin": "8734", "ang": "8736", "and": "8743", "or": "8744", "cap": "8745", - "cup": "8746", "int": "8747", "there4": "8756", "sim": "8764", "cong": "8773", - "asymp": "8776", "ne": "8800", "equiv": "8801", "le": "8804", "ge": "8805", - "sub": "8834", "sup": "8835", "nsub": "8836", "sube": "8838", "supe": "8839", - "oplus": "8853", "otimes": "8855", "perp": "8869", "sdot": "8901", "lceil": "8968", - "rceil": "8969", "lfloor": "8970", "rfloor": "8971", "lang": "9001", "rang": "9002", - "loz": "9674", "spades": "9824", "clubs": "9827", "hearts": "9829", "diams": "9830" +var HTML_ENTITY_MAP = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'" }; - -/** - * Escapes "&", greater than, less than signs, quotation marks, and others into - * the proper XML entities. - * - * @param {String} str The XML string to escape. - * @param {Boolean} strictMode By default, this function attempts to NOT double-escape XML entities. This flag turns that behavior off when set to `true`. - * @return {String} The escaped string - */ -apf.escapeXML = function(str, strictMode) { - if (typeof str != "string") - return str; - if (strictMode) - str = (str || "").replace(/&/g, "&"); - else - str = (str || "").replace(/&(?!#[0-9]{2,5};|[a-zA-Z]{2,};)/g, "&"); - var map = apf.xmlEntityMap; - var isArray = apf.isArray; - return str - .replace(/"/g, """) - .replace(//g, ">") - .replace(/'/g, "'") - .replace(/&([a-zA-Z]+);/gi, function(a, m) { - var x = map[m.toLowerCase()]; - if (x) - return "&#" + (isArray(x) ? x[0] : x) + ";"; - return a; - }); -}; - -/** - * Unescapes `"&"` and other similar XML entities into HTML entities, and then replaces - * 'special' ones (`'`, `>`, `<`, `"`, `&`) into characters - * (`'`, `>`, `<`, `"`, `&`). - * - * @param {String} str The XML string to unescape - * @return {String} The unescaped string - */ -apf.unescapeXML = function(str) { - if (typeof str != "string") - return str; - var map = apf.xmlEntityMapReverse; - var isArray = apf.isArray; - if (!map) { - map = apf.xmlEntityMapReverse = {}; - var origMap = apf.xmlEntityMap; - var keys = Object.keys(origMap); - for (var val, j, l2, i = 0, l = keys.length; i < l; ++i) { - val = origMap[keys[i]]; - if (isArray(val)) { - for (j = 0, l2 = val.length; j < l2; ++j) - map[val[j]] = keys[i]; - } - else - map[val] = keys[i]; - } - } - return str - .replace(/&#([0-9]{2,5});/g, function(a, m) { - var x = map[m]; - if (x) - return "&" + x + ";"; - return a; - }) - .replace(/'/gi, "'") - .replace(/>/gi, ">") - .replace(/</gi, "<") - .replace(/"/gi, "\"") - .replace(/&/gi, "&"); +var HTML_CHARACTERS_EXPRESSION = /[&"'<>]/gm; +apf.escapeHTML = apf.escapeXML = function(text) { + return text && text.replace(HTML_CHARACTERS_EXPRESSION, function(c) { + return HTML_ENTITY_MAP[c] || c; + }); }; /** @@ -6400,7 +6120,7 @@ apf.AmlElement = function(struct, tagName) { if (node.localName == "application") { el = parent; } else { - var namespace = node.prefix === "a" ? apf.aml : apf.xhtml; + var namespace = node.prefix == "a" ? apf.aml : apf.xhtml; var ElementType = namespace.elements[node.localName] || namespace.elements["@default"]; var el = new ElementType({}, node.localName); var a = node.attributes; @@ -6932,9 +6652,9 @@ apf.AmlDocument = function(){ */ this.createElement = function(qualifiedName) { var parts = qualifiedName.split(":"); - var prefix = parts.length === 1 ? "" : parts[0]; - var name = parts.length === 1 ? parts[0]: parts[1]; - var namespace = prefix === "a" ? apf.aml : apf.xhtml; + var prefix = parts.length == 1 ? "" : parts[0]; + var name = parts.length == 1 ? parts[0]: parts[1]; + var namespace = prefix == "a" ? apf.aml : apf.xhtml; var ElementType = namespace.elements[name] || namespace.elements["@default"]; return new ElementType({}, qualifiedName); }; @@ -17675,12 +17395,8 @@ apf.textbox = function(struct, tagName) { this.getValue = function(){ var v; - if (this.isHTMLBox) { - //Chrome has a bug, innerText is cleared when display property is changed - v = apf.html_entity_decode(this.$input.innerHTML - .replace(//g, "\n") - .replace(/<[^>]*>/g, "")); - } + if (this.isHTMLBox) + v = this.$input.innerText; else v = this.$input.value; diff --git a/plugins/c9.vfs.standalone/www/ide.html b/plugins/c9.vfs.standalone/www/ide.html index e20a98f1..7fa80b70 100644 --- a/plugins/c9.vfs.standalone/www/ide.html +++ b/plugins/c9.vfs.standalone/www/ide.html @@ -78,7 +78,7 @@ if (idx == messages.length) idx = messages.length - 1; var msg = messages[idx]; - document.querySelector("#loadingide .cool-message").innerHTML = msg; + document.querySelector("#loadingide .cool-message").textContent = msg; setTimeout(function(){ var s = document.querySelector("#loadingide .status"); From 3edf66c0d14f32e7992aec66445f85802fbdf311 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 30 Jun 2017 00:00:56 +0400 Subject: [PATCH 2/4] add ui.buildDom method --- plugins/c9.ide.ace/ace.js | 7 ++--- plugins/c9.ide.ace/themes.js | 16 +++++----- plugins/c9.ide.collab/collab.js | 8 ++--- plugins/c9.ide.collab/cursor_layer.js | 28 +++++++----------- plugins/c9.ide.ui/lib_apf.js | 42 ++++++++++++++++++++++++++- plugins/c9.ide.ui/ui.js | 4 +++ 6 files changed, 71 insertions(+), 34 deletions(-) diff --git a/plugins/c9.ide.ace/ace.js b/plugins/c9.ide.ace/ace.js index 90399c4c..42b572b0 100644 --- a/plugins/c9.ide.ace/ace.js +++ b/plugins/c9.ide.ace/ace.js @@ -185,10 +185,9 @@ define(function(require, exports, module) { var cssClass = theme.cssClass; - var div = document.createElement("div"); - document.body.appendChild(div); - div.innerHTML = "
"; - div.className = cssClass; + var div = ui.buildDom(["div", { class: cssClass }, [ + "span", { class: "ace_gutter" } + ]], document.body); theme.bg = ui.getStyle(div.firstChild, "backgroundColor"); theme.fg = ui.getStyle(div.firstChild, "color"); diff --git a/plugins/c9.ide.ace/themes.js b/plugins/c9.ide.ace/themes.js index a1b44352..402a63d3 100644 --- a/plugins/c9.ide.ace/themes.js +++ b/plugins/c9.ide.ace/themes.js @@ -180,14 +180,14 @@ define(function(require, exports, module) { rb1.$group.on("afterchange", change); - intro.$int.innerHTML = - '

Themes

You can also style Cloud9 by editing ' - + ' your stylesheet.

' - + '

Set all the colors free!

'; - - intro.$int.querySelector("a").onclick = function() { - configure.editStylesCss(); - }; + ui.buildDom([ + ["h1", null, "Themes"], + ["p", null, "You can also style Cloud9 by editing", + ["a", { href: "javascript:void(0)", onclick: function() { configure.editStylesCss(); } }, + "your stylesheet"] + ], + ["p", { class: "hint" }, "Set all the colors free!"] + ], intro.$int); } /***** Methods *****/ diff --git a/plugins/c9.ide.collab/collab.js b/plugins/c9.ide.collab/collab.js index be592f14..c2f9af0d 100644 --- a/plugins/c9.ide.collab/collab.js +++ b/plugins/c9.ide.collab/collab.js @@ -50,10 +50,10 @@ define(function(require, exports, module) { var emit = plugin.getEmitter(); // open collab documents - var documents = {}; - var openFallbackTimeouts = {}; - var saveFallbackTimeouts = {}; - var usersLeaving = {}; + var documents = Object.create(null); + var openFallbackTimeouts = Object.create(null); + var saveFallbackTimeouts = Object.create(null); + var usersLeaving = Object.create(null); var failedSaveAttempts = 0; var OPEN_FILESYSTEM_FALLBACK_TIMEOUT = 6000; var SAVE_FILESYSTEM_FALLBACK_TIMEOUT = 30000; diff --git a/plugins/c9.ide.collab/cursor_layer.js b/plugins/c9.ide.collab/cursor_layer.js index 280c6047..984f1f78 100644 --- a/plugins/c9.ide.collab/cursor_layer.js +++ b/plugins/c9.ide.collab/cursor_layer.js @@ -1,14 +1,15 @@ /*global define console document apf */ define(function(require, module, exports) { main.consumes = ["Plugin", "ace", "settings", "tabManager", - "collab.util", "collab.workspace", "timeslider"]; + "collab.util", "collab.workspace", "timeslider", "ui"]; main.provides = ["CursorLayer"]; return main; function main(options, imports, register) { - var Plugin = imports.Plugin; var settings = imports.settings; + var Plugin = imports.Plugin; var ace = imports.ace; + var ui = imports.ui; var tabs = imports.tabManager; var util = imports["collab.util"]; var workspace = imports["collab.workspace"]; @@ -254,22 +255,15 @@ define(function(require, module, exports) { } function drawTooltip(selection, fullname) { - var node = document.createElement("div"); - document.body.appendChild(node); + var html = ui.buildDom([ + ["div", { class: "cool_tooltip_cursor", style: "display:none" }, + ["span", { class: "cool_tooltip_cursor_caption" }, fullname] + ], + ["div", { class: "cool_tooltip_cursor_arrow", style: "display:none" }] + ], document.body); - node.className = "cool_tooltip_cursor"; - node.innerHTML = "" + util.escapeHTML(fullname) + ""; - - // create the arrow - var arrow = document.createElement("div"); - document.body.appendChild(arrow); - arrow.className = "cool_tooltip_cursor_arrow"; - - arrow.style.display = "none"; - node.style.display = "none"; - - selection.tooltip = node; - selection.arrow = arrow; + selection.tooltip = html[0]; + selection.arrow = html[1]; } function showTooltip(selection, user, coords) { diff --git a/plugins/c9.ide.ui/lib_apf.js b/plugins/c9.ide.ui/lib_apf.js index d69fd99a..b0f332a2 100644 --- a/plugins/c9.ide.ui/lib_apf.js +++ b/plugins/c9.ide.ui/lib_apf.js @@ -583,7 +583,47 @@ document.documentElement.className += " has_apf"; apf.browserDetect(); - +apf.buildDom = function buildDom(arr, parent) { + if (typeof arr == "string") { + var txt = document.createTextNode(arr); + if (parent) + parent.appendChild(txt); + return txt; + } + + if (!Array.isArray(arr)) + return arr; + if (typeof arr[0] == "object") { + var els = []; + for (var i = 0; i < arr.length; i++) { + var ch = buildDom(arr[i]); + els.push(ch); + if (parent) + parent.appendChild(ch); + } + return els; + } + + var el = document.createElement(arr[0]); + var options = arr[1]; + if (options) { + Object.keys(options).forEach(function(n) { + var val = options[n]; + if (n == "class") { + el.className = Array.isArray(val) ? val.join(" ") : val; + } + else if (typeof val == "function") + el[n] = val; + else + el.setAttribute(n, val); + }); + } + for (var i = 2; i < arr.length; i++) + buildDom(arr[i], el); + if (parent) + parent.appendChild(el); + return el; +}; diff --git a/plugins/c9.ide.ui/ui.js b/plugins/c9.ide.ui/ui.js index a5784d3f..97d0488c 100644 --- a/plugins/c9.ide.ui/ui.js +++ b/plugins/c9.ide.ui/ui.js @@ -573,6 +573,10 @@ define(function(require, module, exports) { */ insertByIndex: insertByIndex, + /** + * + */ + buildDom: apf.buildDom, /** * Escapes "&", greater than, less than signs, quotation marks, From 7ee0dbdbe467c9ff747a8576db8eecae3451d127 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sat, 1 Jul 2017 22:44:15 +0400 Subject: [PATCH 3/4] remove last uses of innerHTML from apf --- plugins/c9.ide.ace/themes.js | 2 +- plugins/c9.ide.collab/collab.js | 10 +- plugins/c9.ide.collab/server/collab-server.js | 12 +- plugins/c9.ide.ui/lib_apf.js | 577 +----------------- 4 files changed, 24 insertions(+), 577 deletions(-) diff --git a/plugins/c9.ide.ace/themes.js b/plugins/c9.ide.ace/themes.js index 402a63d3..17e00cfe 100644 --- a/plugins/c9.ide.ace/themes.js +++ b/plugins/c9.ide.ace/themes.js @@ -182,7 +182,7 @@ define(function(require, exports, module) { ui.buildDom([ ["h1", null, "Themes"], - ["p", null, "You can also style Cloud9 by editing", + ["p", null, "You can also style Cloud9 by editing ", ["a", { href: "javascript:void(0)", onclick: function() { configure.editStylesCss(); } }, "your stylesheet"] ], diff --git a/plugins/c9.ide.collab/collab.js b/plugins/c9.ide.collab/collab.js index c2f9af0d..34445ad8 100644 --- a/plugins/c9.ide.collab/collab.js +++ b/plugins/c9.ide.collab/collab.js @@ -635,13 +635,13 @@ define(function(require, exports, module) { if (!user) return bubble.popup(msg); - var chatName = apf.escapeXML(user.fullname); var md5Email = user.md5Email; - var defaultImgUrl = encodeURIComponent("https://www.aiga.org/uploadedImages/AIGA/Content/About_AIGA/Become_a_member/generic_avatar_300.gif"); console.log("Collab:", user.fullname, msg); - bubble.popup('' + - chatName + '' + msg + ''); + bubble.popup([ + ["img", { width: 26, height: 26, class: "gravatar-image", + src: "https://secure.gravatar.com/avatar/" + md5Email + "?s=26&d=retro" }], + ["span", null, user.fullname, ["span", { class: "notification_sub" }, msg]] + ]); } /***** sync tabs *****/ diff --git a/plugins/c9.ide.collab/server/collab-server.js b/plugins/c9.ide.collab/server/collab-server.js index 71f80900..7312111c 100644 --- a/plugins/c9.ide.collab/server/collab-server.js +++ b/plugins/c9.ide.collab/server/collab-server.js @@ -1039,7 +1039,7 @@ var Store = (function () { // This object should have the following structure: // // { : { : true } } -var documents = {}; +var documents = Object.create(null); // This object should have the following structure: // @@ -1051,7 +1051,7 @@ var watchers; // { : } var clients; -var lastSaveStarts = {}; +var lastSaveStarts = Object.create(null); // SQLite doesn't provide atomic instructions or locks // So this variable expresses in-process locks @@ -2824,7 +2824,7 @@ function createServer() { "Note that visitors of private workspaces can't use collab features"); client.userIds = userIds; - client.openDocIds = {}; + client.openDocIds = Object.create(null); clients[userIds.clientId] = client; // logVerbose("[vfs-collab] Server handshaked", Object.keys(clients).length); @@ -2938,9 +2938,9 @@ function initSocket(userIds, callback) { server.collabInited = false; // init server state - documents = {}; - watchers = {}; - clients = {}; + documents = Object.create(null); + watchers = Object.create(null); + clients = Object.create(null); // Check server installation, init the server and then connect the client to the inited collab server installServer(function (err) { diff --git a/plugins/c9.ide.ui/lib_apf.js b/plugins/c9.ide.ui/lib_apf.js index b0f332a2..624e79f2 100644 --- a/plugins/c9.ide.ui/lib_apf.js +++ b/plugins/c9.ide.ui/lib_apf.js @@ -9647,7 +9647,7 @@ apf.BaseStateButtons = function(){ this.$baseCSSname + "Min"); if (this.btnedit) - oButtons.edit.innerHTML = "close"; //hack + oButtons.edit.textContent = "close"; //hack this.dispatchEvent('editstart'); } @@ -9660,7 +9660,7 @@ apf.BaseStateButtons = function(){ styleClass.unshift(""); if (this.btnedit) - oButtons.edit.innerHTML = "edit"; //hack + oButtons.edit.textContent = "edit"; //hack } } @@ -12236,7 +12236,7 @@ apf.bar = function(struct, tagName) { this.$isLeechingSkin = false; this.$propHandlers["caption"] = function(value) { - this.$int.innerHTML = value; + this.$int.textContent = value; } //@todo apf3.0 refactor @@ -12610,7 +12610,7 @@ apf.button = function(struct, tagName) { this.$setStyleClass(this.$ext, this.$baseCSSname + "Empty"); if (this.oCaption.nodeType == 1) - this.oCaption.innerHTML = String(value || "").trim(); + this.oCaption.textContent = String(value || "").trim(); else this.oCaption.nodeValue = String(value || "").trim(); }; @@ -13081,7 +13081,7 @@ apf.checkbox = function(struct, tagName) { return; if (lbl.nodeType == 1) - lbl.innerHTML = value; + lbl.textContent = value; else lbl.nodeValue = value; }; @@ -13444,7 +13444,7 @@ apf.frame = function(struct, tagName) { if (!this.oCaption) return; if (this.oCaption.nodeType == 1) - this.oCaption.innerHTML = value; + this.oCaption.textContent = value; else this.oCaption.nodeValue = value; }; @@ -13461,20 +13461,6 @@ apf.frame = function(struct, tagName) { apf.skins.setIcon(oIcon, value, this.iconPath); }; - /** - * @attribute {String} icon Sets or gets the URL location (if this is an iframe). - */ - this.$propHandlers["url"] = function(value) { - var node = this.oCaption; - if (node.tagName == "A" || node.nodeType != 1) - node = node.parentNode; - - node.innerHTML = "" - + this.caption + ""; - this.oCaption = this.oCaption.firstChild; - }; - this.$propHandlers["activetitle"] = function(value) { var node = this.oCaption.parentNode; // if (node.nodeType != 1) node = node.parentNode; @@ -13532,238 +13518,6 @@ apf.aml.setElement("frame", apf.frame); - - -/** - * The element displays a picture. This element can read databound resources. - * - * #### Example - * - * This example shows a list with pictures. When one is selected its displayed - * in the `` element: - * - * ```xml, demo - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * ``` - * - * @class apf.img - * @define img - * @media - * @allowchild {smartbinding} - * - * - * @inherits apf.BaseSimple - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.4 - * - */ -/** - * @event click Fires when a user presses a mouse button while over this element. - * - */ -/** - * @binding value Determines the way the value for the element is retrieved - * from the bound data. - * - * #### Example - * - * Sets the image source based on data loaded into this component. - * - * ```xml - * - * - * - * - * ``` - */ -apf.img = function(struct, tagName) { - this.$init(tagName || "img", apf.NODE_VISIBLE, struct); -}; - -apf.preview = function(struct, tagName) { - this.$init(tagName || "preview", apf.NODE_VISIBLE, struct); -}; - -(function(){ - - - /** - * Sets the value of this element. This should be one of the values - * specified in the `values` attribute. - * @param {String} value The new value of this element - */ - this.change = - this.setValue = function(value) { - this.setProperty("value", value, false, true); - }; - - /** - * Returns the current value of this element. - * @return {String} The current image - */ - this.getValue = function(value) { - return this.value; - }; - - - - this.$supportedProperties.push("value", "src"); - /** - * @attribute {String} value Sets or gets the url location of the image displayed. - */ - this.$propHandlers["src"] = - this.$propHandlers["value"] = function(value) { - if (this.oImage.nodeType == 1) - this.oImage.style.backgroundImage = "url(" + value + ")"; - else - this.oImage.nodeValue = value; - - //@todo resize should become a generic thing - if (this.oImage.nodeType == 2 && !this.$resize.done) { - if (this.oImg) { - - //@todo add this to $destroy - var pNode = apf.hasSingleRszEvent ? this.$pHtmlNode : this.$ext; - apf.layout.setRules(pNode, this.$uniqueId + "_image", - "var o = apf.all[" + this.$uniqueId + "];\ - if (o) o.$resize()"); - apf.layout.queue(pNode); - - this.oImg.onload = function(){ - apf.layout.forceResize(pNode); - } - - } - - this.$resize.done = true; - } - - if (this.oImg) { - this.oImg.style.display = value ? "block" : "none"; - - //RLD: disabled lines below for the preview element. the image is probably not loaded yet. - //if (value) - //this.$resize(); - } - }; - - this.refetch = function(){ - this.$propHandlers["value"].call(this, "") - this.$propHandlers["value"].call(this, this.value || this.src) - } - - this.addEventListener("$clear", function(){ - this.value = ""; - - if (this.oImg) - this.oImg.style.display = "none"; - }); - - // *** Init *** // - - this.$draw = function(){ - //Build Main Skin - this.$ext = this.$getExternal(); - this.$ext.onclick = function(e) { - this.host.dispatchEvent("click", {htmlEvent: e || event}); - }; - this.oImage = this.$getLayoutNode("main", "image", this.$ext); - if (this.oImage.nodeType == 1) - this.oImg = this.oImage.getElementsByTagName("img")[0]; - if (this.localName == "preview") { - var _self = this; - this.$ext.onclick = function() { - if (!_self.sPreview) return; - _self.$ext.innerHTML = _self.sPreview; - this.onclick = null; - }; - } - - var _self = this; - apf.addListener(this.$ext, "mouseover", function(e) { - if (!_self.disabled) - _self.dispatchEvent("mouseover", {htmlEvent: e}); - }); - - apf.addListener(this.$ext, "mouseout", function(e) { - if (!_self.disabled) - _self.dispatchEvent("mouseout", {htmlEvent: e}); - }); - }; - - this.addEventListener("DOMNodeInsertedIntoDocument", function() { - var node, - val = "", - i = this.childNodes.length; - - for (; i >= 0; --i) { - if ((node = this.childNodes[i]) && node.nodeName - && node.nodeName == "#cdata-section") { - val = node.nodeValue; - node.removeNode(); - } - } - - this.sPreview = val; - }); - - this.$resize = function(){ - var diff = apf.getDiff(this.$ext); - var wratio = 1, hratio = 1; - - this.oImg.style.width = ""; - this.oImg.style.height = ""; - - if (this.oImg.offsetWidth > this.$ext.offsetWidth) - wratio = this.oImg.offsetWidth / (this.$ext.offsetWidth - diff[0]); - if (this.oImg.offsetHeight > this.$ext.offsetHeight) - hratio = this.oImg.offsetHeight / (this.$ext.offsetHeight - diff[1]); - - if (wratio > hratio && wratio > 1) - this.oImg.style.width = "100%"; - else if (hratio > wratio && hratio > 1) - this.oImg.style.height = "100%"; - - this.oImg.style.top = ((this.$ext.offsetHeight - apf.getHeightDiff(this.$ext) - - this.oImg.offsetHeight) / 2) + "px"; - } -}).call(apf.img.prototype = new apf.BaseSimple()); - -apf.preview.prototype = apf.img.prototype; - -apf.aml.setElement("img", apf.img); -apf.aml.setElement("preview", apf.preview); - - - - - @@ -13872,7 +13626,7 @@ apf.label = function(struct, tagName) { */ this.$supportedProperties.push("caption", "for", "textalign"); this.$propHandlers["caption"] = function(value) { - this.$caption.innerHTML = value; + this.$caption.textContent = value; }; this.$propHandlers["for"] = function(value) { forElement = typeof value == "string" ? self[value] : value; @@ -14755,7 +14509,7 @@ apf.notifier = function(struct, tagName) { this.$setStyleClass(oNoti, this.$baseCSSname + "ShowIcon"); } - oBody.insertAdjacentHTML("beforeend", message || "[No message]"); + apf.buildDom(message || "[No message]", oBody); oNoti.style.display = "block"; oClose.addEventListener("click", function(){ @@ -15463,7 +15217,7 @@ apf.radiobutton = function(struct, tagName) { this.$setStyleClass(this.$ext, this.$baseCSSname + "Empty"); if (this.oLabel) - this.oLabel.innerHTML = value; + this.oLabel.textContent = value; }; /** @@ -16663,313 +16417,6 @@ apf.aml.setElement("splitbutton", apf.splitbutton); - - -/** - * This element displays a rectangle containing arbitrary (X)HTML. - * - * This element can be databound and use databounding rules to - * convert data into (X)HTML using--for instance--XSLT or JSLT. - * - * #### Example: Some simple text - * - * ```xml, demo - * - * - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur congue, nunc sed convallis gravida, justo nunc egestas nisi, eu iaculis nunc ipsum vel orci. Morbi mauris urna, rutrum at imperdiet at, molestie eu risus. Curabitur eu tincidunt eros. Donec in massa ut dolor vulputate commodo. Cras pulvinar urna ut ipsum pulvinar mollis sit amet in dui. Nam lobortis ligula sed tortor dapibus eget tincidunt dui pretium. - * - * - * - * ``` - * - * #### Example: Using Scrolldown - * - * ```xml, demo - * - * - * - * Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur congue, nunc sed convallis gravida, justo nunc egestas nisi, eu iaculis nunc ipsum vel orci. Morbi mauris urna, rutrum at imperdiet at, molestie eu risus. Curabitur eu tincidunt eros. Donec in massa ut dolor vulputate commodo. Cras pulvinar urna ut ipsum pulvinar mollis sit amet in dui. Nam lobortis ligula sed tortor dapibus eget tincidunt dui pretium. Quisque semper sem dignissim quam ullamcorper et lobortis arcu eleifend. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce ac commodo mi. Pellentesque sit amet magna sed velit volutpat volutpat. Nam lobortis sem sed tortor accumsan dictum. Donec scelerisque rhoncus cursus. Mauris dui dolor, vehicula quis lacinia quis, facilisis et eros. Nulla facilisi. Donec urna velit, adipiscing non sollicitudin at, sodales id lorem. Fusce fringilla, magna id pellentesque egestas, neque risus luctus mauris, vel porttitor libero tortor et augue. Integer euismod porttitor mi, at viverra nulla pharetra vel. Etiam odio elit, volutpat a porttitor eu, posuere nec augue. Phasellus placerat lacus ut augue tempor consectetur. - * - * - * Add a line - * - * - * - * ``` - * - * @class apf.text - * @define text - * - * @form - * @inherits apf.Cache - * @inherits apf.StandardBinding - * - * @author Ruben Daniels (ruben AT ajax DOT org) - * @version %I%, %G% - * @since 0.1 - * - */ -// @todo Please refactor this object -apf.text = function(struct, tagName) { - this.$init(tagName || "text", apf.NODE_VISIBLE, struct); - - this.$nodes = []; -}; - -(function(){ - - this.$focussable = true; // This object can't get the focus - this.focussable = false; - this.textselect = true; - this.$hasStateMessages = true; - - this.$textTimer = this.$lastMsg = this.$lastClass = this.$changedHeight = null; - - // *** Properties and Attributes *** // - - /** - * @attribute {Boolean} scrolldown Sets or gets whether this element's viewport is always - * scrolled down. This is especially useful - * when this element is used to displayed - * streaming content such as a chat conversation. - * - */ - /** - * @attribute {Boolean} secure Sets or gets whether the content loaded in this element - * should be filtered in order for it to not - * be able to execute JavaScript. This is - * especially useful when the content does - * not come from a trusted source, like a - * web service or xmpp feed. - */ - this.$booleanProperties["scrolldown"] = true; - this.$booleanProperties["secure"] = true; - this.$booleanProperties["textselect"] = true; - this.$supportedProperties.push("behavior", "scrolldown", "secure", "value"); - - this.$isTextInput = function(){ - return this.textselect; - } - - this.$propHandlers["scrolldown"] = function(value) { - var _self = this; - - if (value) { - //this.addEventListener("resize", this.$resize); - this.$scrolldown = true; - apf.addListener(this.$scrollArea, "scroll", this.$scrollFunc = function(){ - _self.$scrolldown = this.scrollTop >= this.scrollHeight - - this.offsetHeight + apf.getVerBorders(this); - }); - this.addEventListener("scroll", this.$scroll); - this.addEventListener("afterload", this.$scroll); - this.addEventListener("resize", function(){ - if (_self.$scrollArea && _self.$scrolldown && _self.scrolldown) - _self.$scrollArea.scrollTop = _self.$scrollArea.scrollHeight; - }); - clearInterval(this.$textTimer); - this.$textTimer = setInterval(function(){ - if (_self.$scrollArea && _self.$scrolldown && _self.scrolldown) - _self.$scrollArea.scrollTop = _self.$scrollArea.scrollHeight; - }, 200); - } - else { - //this.removeEventListener("resize", this.$resize); - - this.removeEventListener("scroll", this.$scroll); - this.removeEventListener("afterload", this.$scroll); - clearInterval(this.$textTimer); - if (this.$scrollArea) - apf.removeListener(this.$scrollArea, "scoll", this.$scrollFunc); - } - } - - this.$scroll = function(e) { - var html = this.$scrollArea; - - if (e.name == "afterload") { - this.$scrolldown = true; - html.scrollTop = html.scrollHeight; - return; - } - - this.$scrolldown = html.scrollTop >= html.scrollHeight - - html.offsetHeight + apf.getVerBorders(html); - }; - - /*this.$resize = function(){ - if (this.scrolldown && this.$scrolldown) - this.$scrollArea.scrollTop = this.$scrollArea.scrollHeight; - }*/ - - /** - * @attribute {String} value Sets or gets the contents of this element. This can be text, html, xhtml. - */ - this.$propHandlers["value"] = function(value, prop, force, forceAdd) { - if (this.each) - return; - - if (typeof value != "string") { - if (value.nodeType) - value = value.nodeType > 1 && value.nodeType < 5 - ? value.nodeValue - : value.firstChild && value.firstChild.nodeValue || ""; - else - value = value ? value.toString() : ""; - } - - if (this.secure) { - value = value.replace(//g, "") - .replace(//g, "") - .replace(new RegExp("ondblclick|onclick|onmouseover|onmouseout" - + "|onmousedown|onmousemove|onkeypress|onkeydown|onkeyup|onchange" - + "|onpropertychange", "g"), "ona"); - } - - value = value.replace(/\<\?xml version="1\.0" encoding="UTF-16"\?\>/, ""); - - if (forceAdd) { - apf.insertHtmlNodes(null, this.$container, null, value); - if (!this.value) this.value = ""; - this.value += value; - } - else - this.$container.innerHTML = value; - - if (this.scrolldown && this.$scrolldown) - this.$scrollArea.scrollTop = this.$scrollArea.scrollHeight; - }; - - - this.addEventListener("$clear", function(){ - this.$container.innerHTML = ""; - this.value = ""; - this.dispatchEvent("prop.value", {value: ""}); - }); - - // @todo replace this stub with something that does something - this.$moveNode = function() {}; - - // *** Public methods *** // - - - - this.addValue = function(value) { - this.$propHandlers["value"].call(this, value, null, null, true); - this.dispatchEvent("prop.value", {value: this.value}); - }; - - /** - * Sets the value of this element. This should be one of the values - * specified in the `values` attribute. - * @param {String} value The new value of this element - */ - this.change = - this.setValue = function(value) { - this.setProperty("value", value, false, true); - }; - - /** - * Returns the current value of this element. - * @return {String} The current value. - */ - this.getValue = function(){ - return this.$container.innerHTML; - }; - - - - // *** Keyboard Support *** // - - - this.addEventListener("keydown", function(e) { - var key = e.keyCode; - - switch (key) { - case 33: - //PGUP - this.$container.scrollTop -= this.$container.offsetHeight; - break; - case 34: - //PGDN - this.$container.scrollTop += this.$container.offsetHeight; - break; - case 35: - //END - this.$container.scrollTop = this.$container.scrollHeight; - break; - case 36: - //HOME - this.$container.scrollTop = 0; - break; - case 38: - this.$container.scrollTop -= 10; - break; - case 40: - this.$container.scrollTop += 10; - break; - default: - return; - } - - return false; - }, true); - - - // *** Private methods *** // - - this.$fill = function(){ - //apf.insertHtmlNode(null, this.$container, null, this.$nodes.join("")); - this.$container.insertAdjacentHTML("beforeend", this.$nodes.join("")); - this.$nodes = []; - } - - this.$deInitNode = - this.$updateNode = - this.$moveNode = apf.K; - - // *** Init *** // - - this.$draw = function(){ - this.$ext = this.$getExternal(); - this.$container = this.$getLayoutNode("main", "container", this.$ext); - - this.$scrollArea = this.oFocus ? this.oFocus.parentNode : this.$container; - - if (this.$container.tagName.toLowerCase() == "iframe") { - var node = document.createElement("div"); - this.$ext.parentNode.replaceChild(node, this.$ext); - node.className = this.$ext.className; - this.$ext = this.$container = node; - } - }; - - this.addEventListener("DOMNodeRemovedFromDocument", function() { - clearInterval(this.$textTimer); - apf.destroyHtmlNode(this.oDrag); - - if (this.$scrollArea) - this.$scrollArea.onscoll = this.$scrollArea = null; - - this.oDrag = this.oIframe = this.oFocus = this.$container = this.$ext = null; - }); -}).call(apf.text.prototype = new apf.StandardBinding()); - -apf.aml.setElement("text", apf.text); - - - - - - - - //@todo DOCUMENT the modules too @@ -17189,8 +16636,8 @@ apf.textbox = function(struct, tagName) { if (!initial && !value && !this.hasFocus()) //@todo apf3.x research the use of clear return this.$clear(); else if (this.isHTMLBox) { - if (this.$input.innerHTML != value) - this.$input.innerHTML = value; + if (this.$input.textContent != value) + this.$input.textContent = value; } else if (this.$input.value != value) this.$input.value = value; @@ -17328,7 +16775,7 @@ apf.textbox = function(struct, tagName) { this.$clear(true); if (this.type == "password" && this.$inputInitFix) { - this.$inputInitFix.innerHTML = value; + this.$inputInitFix.textContent = value; apf.setStyleClass(this.$inputInitFix, "initFxEnabled"); } }; From 6a5cc4c6c8284dde5e3831686078cfd197203000 Mon Sep 17 00:00:00 2001 From: nightwing Date: Sun, 2 Jul 2017 11:34:35 +0400 Subject: [PATCH 4/4] restore apf.img since it was used in a test --- plugins/c9.ide.collab/chat/chat.js | 4 +- plugins/c9.ide.editors/imgview.js | 2 +- plugins/c9.ide.ui/lib_apf.js | 170 +++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 3 deletions(-) diff --git a/plugins/c9.ide.collab/chat/chat.js b/plugins/c9.ide.collab/chat/chat.js index f416a50a..77adddf5 100644 --- a/plugins/c9.ide.collab/chat/chat.js +++ b/plugins/c9.ide.collab/chat/chat.js @@ -273,8 +273,8 @@ define(function(require, exports, module) { } if (msg.increment) { - var count = Number(chatCounter.innerHTML); - chatCounter.innerHTML = count + 1; + var count = Number(chatCounter.textContent); + chatCounter.textContent = count + 1; } var inputFocussed = chatInput && chatInput.ace.isFocused(); diff --git a/plugins/c9.ide.editors/imgview.js b/plugins/c9.ide.editors/imgview.js index 28c837a3..7bf95b6d 100644 --- a/plugins/c9.ide.editors/imgview.js +++ b/plugins/c9.ide.editors/imgview.js @@ -60,7 +60,7 @@ define(function(require, exports, module) { ? path : vfs.url(path); - editor.setProperty("value", apf.escapeXML(fullpath)); + editor.setProperty("value", fullpath); } /***** Lifecycle *****/ diff --git a/plugins/c9.ide.ui/lib_apf.js b/plugins/c9.ide.ui/lib_apf.js index 624e79f2..7ce68c9a 100644 --- a/plugins/c9.ide.ui/lib_apf.js +++ b/plugins/c9.ide.ui/lib_apf.js @@ -13519,6 +13519,176 @@ apf.aml.setElement("frame", apf.frame); +/** + * @event click Fires when a user presses a mouse button while over this element. + * + */ +/** + * @binding value Determines the way the value for the element is retrieved + * from the bound data. + * + * #### Example + * + * Sets the image source based on data loaded into this component. + * + * ```xml + * + * + * + * + * ``` + */ +apf.img = function(struct, tagName) { + this.$init(tagName || "img", apf.NODE_VISIBLE, struct); +}; + +(function(){ + + + /** + * Sets the value of this element. This should be one of the values + * specified in the `values` attribute. + * @param {String} value The new value of this element + */ + this.change = + this.setValue = function(value) { + this.setProperty("value", value, false, true); + }; + + /** + * Returns the current value of this element. + * @return {String} The current image + */ + this.getValue = function(value) { + return this.value; + }; + + + + this.$supportedProperties.push("value", "src"); + /** + * @attribute {String} value Sets or gets the url location of the image displayed. + */ + this.$propHandlers["src"] = + this.$propHandlers["value"] = function(value) { + if (this.oImage.nodeType == 1) + this.oImage.style.backgroundImage = "url(" + value + ")"; + else + this.oImage.nodeValue = value; + + //@todo resize should become a generic thing + if (this.oImage.nodeType == 2 && !this.$resize.done) { + if (this.oImg) { + + //@todo add this to $destroy + var pNode = apf.hasSingleRszEvent ? this.$pHtmlNode : this.$ext; + apf.layout.setRules(pNode, this.$uniqueId + "_image", + "var o = apf.all[" + this.$uniqueId + "];\ + if (o) o.$resize()"); + apf.layout.queue(pNode); + + this.oImg.onload = function(){ + apf.layout.forceResize(pNode); + } + + } + + this.$resize.done = true; + } + + if (this.oImg) { + this.oImg.style.display = value ? "block" : "none"; + + //RLD: disabled lines below for the preview element. the image is probably not loaded yet. + //if (value) + //this.$resize(); + } + }; + + this.refetch = function(){ + this.$propHandlers["value"].call(this, "") + this.$propHandlers["value"].call(this, this.value || this.src) + } + + this.addEventListener("$clear", function(){ + this.value = ""; + + if (this.oImg) + this.oImg.style.display = "none"; + }); + + // *** Init *** // + + this.$draw = function(){ + //Build Main Skin + this.$ext = this.$getExternal(); + this.$ext.onclick = function(e) { + this.host.dispatchEvent("click", {htmlEvent: e || event}); + }; + this.oImage = this.$getLayoutNode("main", "image", this.$ext); + if (this.oImage.nodeType == 1) + this.oImg = this.oImage.getElementsByTagName("img")[0]; + + var _self = this; + apf.addListener(this.$ext, "mouseover", function(e) { + if (!_self.disabled) + _self.dispatchEvent("mouseover", {htmlEvent: e}); + }); + + apf.addListener(this.$ext, "mouseout", function(e) { + if (!_self.disabled) + _self.dispatchEvent("mouseout", {htmlEvent: e}); + }); + }; + + this.addEventListener("DOMNodeInsertedIntoDocument", function() { + var node, + val = "", + i = this.childNodes.length; + + for (; i >= 0; --i) { + if ((node = this.childNodes[i]) && node.nodeName + && node.nodeName == "#cdata-section") { + val = node.nodeValue; + node.removeNode(); + } + } + + this.sPreview = val; + }); + + this.$resize = function(){ + var diff = apf.getDiff(this.$ext); + var wratio = 1, hratio = 1; + + this.oImg.style.width = ""; + this.oImg.style.height = ""; + + if (this.oImg.offsetWidth > this.$ext.offsetWidth) + wratio = this.oImg.offsetWidth / (this.$ext.offsetWidth - diff[0]); + if (this.oImg.offsetHeight > this.$ext.offsetHeight) + hratio = this.oImg.offsetHeight / (this.$ext.offsetHeight - diff[1]); + + if (wratio > hratio && wratio > 1) + this.oImg.style.width = "100%"; + else if (hratio > wratio && hratio > 1) + this.oImg.style.height = "100%"; + + this.oImg.style.top = ((this.$ext.offsetHeight - apf.getHeightDiff(this.$ext) + - this.oImg.offsetHeight) / 2) + "px"; + } +}).call(apf.img.prototype = new apf.BaseSimple()); + + +apf.aml.setElement("img", apf.img); + + + +