From 96b7b5e8d0c32aa09cc8d1b9fec66c68d239f92d Mon Sep 17 00:00:00 2001 From: Swen Date: Mon, 14 Jan 2013 21:12:40 +0100 Subject: [PATCH 001/158] Removed unnecessary return statement --- src/node/db/PadManager.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node/db/PadManager.js b/src/node/db/PadManager.js index 8cd69a83e..76b2db84b 100644 --- a/src/node/db/PadManager.js +++ b/src/node/db/PadManager.js @@ -56,7 +56,6 @@ var padList = { }); } }); - return this; }, /** * Returns all pads in alphabetical order as array. From 80333d8e0a60017b5625ac885ff66a41d5ef01b8 Mon Sep 17 00:00:00 2001 From: spcsser Date: Sun, 27 Jan 2013 23:02:27 +0100 Subject: [PATCH 002/158] Added ability to add menu entries for admin pages. --- src/static/css/admin.css | 1 + src/templates/admin/index.html | 5 +++-- src/templates/admin/plugins-info.html | 10 +++++++--- src/templates/admin/plugins.html | 13 ++++++++----- src/templates/admin/settings.html | 10 +++++++--- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/static/css/admin.css b/src/static/css/admin.css index 3a7291516..9ea02744f 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -42,6 +42,7 @@ div.innerwrapper { border-radius: 0 0 7px 7px; margin-left:250px; min-width:400px; + width:100%; } #wrapper { diff --git a/src/templates/admin/index.html b/src/templates/admin/index.html index 16ea84279..8827f1957 100644 --- a/src/templates/admin/index.html +++ b/src/templates/admin/index.html @@ -9,12 +9,13 @@
-
diff --git a/src/templates/admin/plugins-info.html b/src/templates/admin/plugins-info.html index 605b23d38..648e7d6d0 100644 --- a/src/templates/admin/plugins-info.html +++ b/src/templates/admin/plugins-info.html @@ -12,9 +12,13 @@
diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index a85db557a..bfe535e7e 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -19,11 +19,14 @@ <% } %> diff --git a/src/templates/admin/settings.html b/src/templates/admin/settings.html index be262f243..0fb1c9ae1 100644 --- a/src/templates/admin/settings.html +++ b/src/templates/admin/settings.html @@ -24,9 +24,13 @@
From 7f5579c2ed069d502af8b6c2e7ef2fff479417bf Mon Sep 17 00:00:00 2001 From: Swen Date: Wed, 30 Jan 2013 20:21:21 +0100 Subject: [PATCH 003/158] Reverted change --- src/node/db/PadManager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node/db/PadManager.js b/src/node/db/PadManager.js index c3d862246..5e0af4643 100644 --- a/src/node/db/PadManager.js +++ b/src/node/db/PadManager.js @@ -56,6 +56,7 @@ var padList = { }); } }); + return this; }, /** * Returns all pads in alphabetical order as array. From 360d5c150be96f8532d15eada1a49392dd2c0dbb Mon Sep 17 00:00:00 2001 From: Swen Date: Wed, 30 Jan 2013 20:24:48 +0100 Subject: [PATCH 004/158] Update html code to proper validate --- src/templates/admin/index.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/templates/admin/index.html b/src/templates/admin/index.html index 8827f1957..0e5c16edd 100644 --- a/src/templates/admin/index.html +++ b/src/templates/admin/index.html @@ -10,11 +10,13 @@
From 037478cbcd1b29b99dd4f17dd88e7d90ce63152a Mon Sep 17 00:00:00 2001 From: mluto Date: Thu, 31 Jan 2013 19:05:23 +0100 Subject: [PATCH 005/158] Made plugin search not casesensitive --- src/static/js/pluginfw/installer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/static/js/pluginfw/installer.js b/src/static/js/pluginfw/installer.js index eb10f8afd..15d879409 100644 --- a/src/static/js/pluginfw/installer.js +++ b/src/static/js/pluginfw/installer.js @@ -94,11 +94,12 @@ exports.search = function(query, cache, cb) { if (er) return cb(er); var res = {}; var i = 0; + var pattern = query.pattern.toLowerCase(); for (key in data) { // for every plugin in the data from npm if ( key.indexOf(plugins.prefix) == 0 - && key.indexOf(query.pattern) != -1 + && key.indexOf(pattern) != -1 || key.indexOf(plugins.prefix) == 0 - && data[key].description.indexOf(query.pattern) != -1 + && data[key].description.indexOf(pattern) != -1 ) { // If the name contains ep_ and the search string is in the name or description i++; if (i > query.offset From b9b5ebf6610978a9d8d0785e2baf74a42009a2a0 Mon Sep 17 00:00:00 2001 From: Manuel Knitza Date: Fri, 1 Feb 2013 21:28:22 +0100 Subject: [PATCH 006/158] Update src/static/js/html10n.js Added missing semicolons (#1440) --- src/static/js/html10n.js | 230 +++++++++++++++++++-------------------- 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/src/static/js/html10n.js b/src/static/js/html10n.js index d0d14814b..e1c025c43 100644 --- a/src/static/js/html10n.js +++ b/src/static/js/html10n.js @@ -23,27 +23,27 @@ window.html10n = (function(window, document, undefined) { // fix console - var console = window.console + var console = window.console; function interceptConsole(method){ - if (!console) return function() {} + if (!console) return function() {}; - var original = console[method] + var original = console[method]; // do sneaky stuff if (original.bind){ // Do this for normal browsers - return original.bind(console) + return original.bind(console); }else{ return function() { // Do this for IE - var message = Array.prototype.slice.apply(arguments).join(' ') - original(message) + var message = Array.prototype.slice.apply(arguments).join(' '); + original(message); } } } var consoleLog = interceptConsole('log') , consoleWarn = interceptConsole('warn') - , consoleError = interceptConsole('warn') + , consoleError = interceptConsole('warn'); // fix Array.prototype.instanceOf in, guess what, IE! <3 @@ -84,14 +84,14 @@ window.html10n = (function(window, document, undefined) { * MicroEvent - to make any js object an event emitter (server or browser) */ - var MicroEvent = function(){} + var MicroEvent = function(){} MicroEvent.prototype = { - bind : function(event, fct){ + bind : function(event, fct){ this._events = this._events || {}; this._events[event] = this._events[event] || []; this._events[event].push(fct); }, - unbind : function(event, fct){ + unbind : function(event, fct){ this._events = this._events || {}; if( event in this._events === false ) return; this._events[event].splice(this._events[event].indexOf(fct), 1); @@ -100,7 +100,7 @@ window.html10n = (function(window, document, undefined) { this._events = this._events || {}; if( event in this._events === false ) return; for(var i = 0; i < this._events[event].length; i++){ - this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)) + this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)); } } }; @@ -122,50 +122,50 @@ window.html10n = (function(window, document, undefined) { * and caching all necessary resources */ function Loader(resources) { - this.resources = resources - this.cache = {} // file => contents - this.langs = {} // lang => strings + this.resources = resources; + this.cache = {}; // file => contents + this.langs = {}; // lang => strings } Loader.prototype.load = function(lang, cb) { - if(this.langs[lang]) return cb() + if(this.langs[lang]) return cb(); if (this.resources.length > 0) { var reqs = 0; for (var i=0, n=this.resources.length; i < n; i++) { this.fetch(this.resources[i], lang, function(e) { reqs++; - if(e) return setTimeout(function(){ throw e }, 0) + if(e) return setTimeout(function(){ throw e }, 0); if (reqs < n) return;// Call back once all reqs are completed - cb && cb() + cb && cb(); }) } } } Loader.prototype.fetch = function(href, lang, cb) { - var that = this + var that = this; if (this.cache[href]) { this.parse(lang, href, this.cache[href], cb) return; } - var xhr = new XMLHttpRequest() - xhr.open('GET', href, /*async: */true) + var xhr = new XMLHttpRequest(); + xhr.open('GET', href, /*async: */true); if (xhr.overrideMimeType) { xhr.overrideMimeType('application/json; charset=utf-8'); } xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200 || xhr.status === 0) { - var data = JSON.parse(xhr.responseText) - that.cache[href] = data + var data = JSON.parse(xhr.responseText); + that.cache[href] = data; // Pass on the contents for parsing - that.parse(lang, href, data, cb) + that.parse(lang, href, data, cb); } else { - cb(new Error('Failed to load '+href)) + cb(new Error('Failed to load '+href)); } } }; @@ -174,39 +174,39 @@ window.html10n = (function(window, document, undefined) { Loader.prototype.parse = function(lang, currHref, data, cb) { if ('object' != typeof data) { - cb(new Error('A file couldn\'t be parsed as json.')) - return + cb(new Error('A file couldn\'t be parsed as json.')); + return; } - if (!data[lang]) lang = lang.substr(0, lang.indexOf('-') == -1? lang.length : lang.indexOf('-')) + if (!data[lang]) lang = lang.substr(0, lang.indexOf('-') == -1? lang.length : lang.indexOf('-')); if (!data[lang]) { - cb(new Error('Couldn\'t find translations for '+lang)) - return + cb(new Error('Couldn\'t find translations for '+lang)); + return; } if ('string' == typeof data[lang]) { // Import rule // absolute path - var importUrl = data[lang] + var importUrl = data[lang]; // relative path if(data[lang].indexOf("http") != 0 && data[lang].indexOf("/") != 0) { - importUrl = currHref+"/../"+data[lang] + importUrl = currHref+"/../"+data[lang]; } - this.fetch(importUrl, lang, cb) - return + this.fetch(importUrl, lang, cb); + return; } if ('object' != typeof data[lang]) { - cb(new Error('Translations should be specified as JSON objects!')) - return + cb(new Error('Translations should be specified as JSON objects!')); + return; } - this.langs[lang] = data[lang] + this.langs[lang] = data[lang]; // TODO: Also store accompanying langs - cb() + cb(); } @@ -216,11 +216,11 @@ window.html10n = (function(window, document, undefined) { var html10n = { language : null } - MicroEvent.mixin(html10n) + MicroEvent.mixin(html10n); - html10n.macros = {} + html10n.macros = {}; - html10n.rtl = ["ar","dv","fa","ha","he","ks","ku","ps","ur","yi"] + html10n.rtl = ["ar","dv","fa","ha","he","ks","ku","ps","ur","yi"]; /** * Get rules for plural forms (shared with JetPack), see: @@ -664,14 +664,14 @@ window.html10n = (function(window, document, undefined) { * @param langs An array of lang codes defining fallbacks */ html10n.localize = function(langs) { - var that = this + var that = this; // if only one string => create an array - if ('string' == typeof langs) langs = [langs] + if ('string' == typeof langs) langs = [langs]; this.build(langs, function(er, translations) { - html10n.translations = translations - html10n.translateElement(translations) - that.trigger('localized') + html10n.translations = translations; + html10n.translateElement(translations); + that.trigger('localized'); }) } @@ -682,78 +682,78 @@ window.html10n = (function(window, document, undefined) { * @param element A DOM element, if omitted, the document element will be used */ html10n.translateElement = function(translations, element) { - element = element || document.documentElement + element = element || document.documentElement; var children = element? getTranslatableChildren(element) : document.childNodes; for (var i=0, n=children.length; i < n; i++) { - this.translateNode(translations, children[i]) + this.translateNode(translations, children[i]); } // translate element itself if necessary - this.translateNode(translations, element) + this.translateNode(translations, element); } function asyncForEach(list, iterator, cb) { var i = 0 - , n = list.length + , n = list.length; iterator(list[i], i, function each(err) { - if(err) consoleLog(err) - i++ + if(err) consoleLog(err); + i++; if (i < n) return iterator(list[i],i, each); - cb() + cb(); }) } function getTranslatableChildren(element) { if(!document.querySelectorAll) { - if (!element) return [] + if (!element) return []; var nodes = element.getElementsByTagName('*') - , l10nElements = [] + , l10nElements = []; for (var i=0, n=nodes.length; i < n; i++) { if (nodes[i].getAttribute('data-l10n-id')) l10nElements.push(nodes[i]); } - return l10nElements + return l10nElements; } - return element.querySelectorAll('*[data-l10n-id]') + return element.querySelectorAll('*[data-l10n-id]'); } html10n.get = function(id, args) { - var translations = html10n.translations - if(!translations) return consoleWarn('No translations available (yet)') - if(!translations[id]) return consoleWarn('Could not find string '+id) + var translations = html10n.translations; + if(!translations) return consoleWarn('No translations available (yet)'); + if(!translations[id]) return consoleWarn('Could not find string '+id); // apply args - var str = substArguments(translations[id], args) + var str = substArguments(translations[id], args); // apply macros - return substMacros(id, str, args) + return substMacros(id, str, args); // replace {{arguments}} with their values or the // associated translation string (based on its key) function substArguments(str, args) { var reArgs = /\{\{\s*([a-zA-Z\.]+)\s*\}\}/ - , match + , match; while (match = reArgs.exec(str)) { if (!match || match.length < 2) - return str // argument key not found + return str; // argument key not found var arg = match[1] - , sub = '' + , sub = ''; if (arg in args) { - sub = args[arg] + sub = args[arg]; } else if (arg in translations) { - sub = translations[arg] + sub = translations[arg]; } else { - consoleWarn('Could not find argument {{' + arg + '}}') - return str + consoleWarn('Could not find argument {{' + arg + '}}'); + return str; } - str = str.substring(0, match.index) + sub + str.substr(match.index + match[0].length) + str = str.substring(0, match.index) + sub + str.substr(match.index + match[0].length); } - return str + return str; } // replace {[macros]} with their values @@ -766,21 +766,21 @@ window.html10n = (function(window, document, undefined) { // a macro has been found // Note: at the moment, only one parameter is supported var macroName = reMatch[1] - , paramName = reMatch[2] + , paramName = reMatch[2]; - if (!(macroName in gMacros)) return str + if (!(macroName in gMacros)) return str; - var param + var param; if (args && paramName in args) { - param = args[paramName] + param = args[paramName]; } else if (paramName in translations) { - param = translations[paramName] + param = translations[paramName]; } // there's no macro parser yet: it has to be defined in gMacros - var macro = html10n.macros[macroName] - str = macro(translations, key, str, param) - return str + var macro = html10n.macros[macroName]; + str = macro(translations, key, str, param); + return str; } } @@ -788,26 +788,26 @@ window.html10n = (function(window, document, undefined) { * Applies translations to a DOM node (recursive) */ html10n.translateNode = function(translations, node) { - var str = {} + var str = {}; // get id - str.id = node.getAttribute('data-l10n-id') - if (!str.id) return + str.id = node.getAttribute('data-l10n-id'); + if (!str.id) return; - if(!translations[str.id]) return consoleWarn('Couldn\'t find translation key '+str.id) + if(!translations[str.id]) return consoleWarn('Couldn\'t find translation key '+str.id); // get args if(window.JSON) { - str.args = JSON.parse(node.getAttribute('data-l10n-args')) + str.args = JSON.parse(node.getAttribute('data-l10n-args')); }else{ try{ - str.args = eval(node.getAttribute('data-l10n-args')) + str.args = eval(node.getAttribute('data-l10n-args')); }catch(e) { - consoleWarn('Couldn\'t parse args for '+str.id) + consoleWarn('Couldn\'t parse args for '+str.id); } } - str.str = html10n.get(str.id, str.args) + str.str = html10n.get(str.id, str.args); // get attribute name to apply str to var prop @@ -817,31 +817,31 @@ window.html10n = (function(window, document, undefined) { , "innerHTML": 1 , "alt": 1 , "textContent": 1 - } + }; if (index > 0 && str.id.substr(index + 1) in attrList) { // an attribute has been specified - prop = str.id.substr(index + 1) + prop = str.id.substr(index + 1); } else { // no attribute: assuming text content by default - prop = document.body.textContent ? 'textContent' : 'innerText' + prop = document.body.textContent ? 'textContent' : 'innerText'; } // Apply translation if (node.children.length === 0 || prop != 'textContent') { - node[prop] = str.str + node[prop] = str.str; } else { var children = node.childNodes, - found = false + found = false; for (var i=0, n=children.length; i < n; i++) { if (children[i].nodeType === 3 && /\S/.test(children[i].textContent)) { if (!found) { - children[i].nodeValue = str.str - found = true + children[i].nodeValue = str.str; + found = true; } else { - children[i].nodeValue = '' + children[i].nodeValue = ''; } } } if (!found) { - consoleWarn('Unexpected error: could not translate element content for key '+str.id, node) + consoleWarn('Unexpected error: could not translate element content for key '+str.id, node); } } } @@ -852,32 +852,32 @@ window.html10n = (function(window, document, undefined) { */ html10n.build = function(langs, cb) { var that = this - , build = {} + , build = {}; asyncForEach(langs, function (lang, i, next) { if(!lang) return next(); - that.loader.load(lang, next) + that.loader.load(lang, next); }, function() { - var lang - langs.reverse() + var lang; + langs.reverse(); // loop through priority array... for (var i=0, n=langs.length; i < n; i++) { - lang = langs[i] + lang = langs[i]; if(!lang || !(lang in that.loader.langs)) continue; // ... and apply all strings of the current lang in the list // to our build object for (var string in that.loader.langs[lang]) { - build[string] = that.loader.langs[lang][string] + build[string] = that.loader.langs[lang][string]; } // the last applied lang will be exposed as the // lang the page was translated to - that.language = lang + that.language = lang; } - cb(null, build) + cb(null, build); }) } @@ -893,8 +893,8 @@ window.html10n = (function(window, document, undefined) { * Returns the direction of the language returned be html10n#getLanguage */ html10n.getDirection = function() { - var langCode = this.language.indexOf('-') == -1? this.language : this.language.substr(0, this.language.indexOf('-')) - return html10n.rtl.indexOf(langCode) == -1? 'ltr' : 'rtl' + var langCode = this.language.indexOf('-') == -1? this.language : this.language.substr(0, this.language.indexOf('-')); + return html10n.rtl.indexOf(langCode) == -1? 'ltr' : 'rtl'; } /** @@ -903,28 +903,28 @@ window.html10n = (function(window, document, undefined) { html10n.index = function () { // Find all s var links = document.getElementsByTagName('link') - , resources = [] + , resources = []; for (var i=0, n=links.length; i < n; i++) { if (links[i].type != 'application/l10n+json') continue; - resources.push(links[i].href) + resources.push(links[i].href); } - this.loader = new Loader(resources) - this.trigger('indexed') + this.loader = new Loader(resources); + this.trigger('indexed'); } if (document.addEventListener) // modern browsers and IE9+ document.addEventListener('DOMContentLoaded', function() { - html10n.index() - }, false) + html10n.index(); + }, false); else if (window.attachEvent) window.attachEvent('onload', function() { - html10n.index() - }, false) + html10n.index(); + }, false); // gettext-like shortcut if (window._ === undefined) window._ = html10n.get; - return html10n -})(window, document) \ No newline at end of file + return html10n; +})(window, document); From 594d27233449979ff0e4107e1e3a312cc1c3f9e7 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 3 Feb 2013 00:14:17 +0000 Subject: [PATCH 007/158] allow plugins to specify frontend test specs --- src/node/hooks/express/tests.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index 94cd5fb62..50fef8004 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -5,6 +5,17 @@ var path = require("path") exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/tests/frontend/specs_list.js', function(req, res){ fs.readdir('tests/frontend/specs', function(err, files){ + + fs.readdir('node_modules', function(err, plugins){ // installed plugins + plugins.forEach(function(plugin){ // for each one + if(fs.existsSync("node_modules/"+plugin+"/tests/frontend/specs")){ // If the folder exists + fs.readdir("node_modules/"+plugin+"/tests/frontend/specs/", function(err, pluginFiles){ + files.push(pluginFiles); + }); + } + }); + }); + if(err){ return res.send(500); } res.send("var specs_list = " + JSON.stringify(files.sort()) + ";\n"); @@ -44,4 +55,4 @@ exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/tests/frontend', function (req, res) { res.redirect('/tests/frontend/'); }); -} \ No newline at end of file +} From 0ff9f53297d178da52f761b5f5d737264fadb7b1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 3 Feb 2013 00:18:24 +0000 Subject: [PATCH 008/158] correct path --- src/node/hooks/express/tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index 50fef8004..9aeaefe6a 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -10,7 +10,7 @@ exports.expressCreateServer = function (hook_name, args, cb) { plugins.forEach(function(plugin){ // for each one if(fs.existsSync("node_modules/"+plugin+"/tests/frontend/specs")){ // If the folder exists fs.readdir("node_modules/"+plugin+"/tests/frontend/specs/", function(err, pluginFiles){ - files.push(pluginFiles); + files.push("/static/plugins/"+plugin+"/tests/frontend/specs/"+pluginFiles); }); } }); From d30daccb10ac35690fc8bffa6723e41ae1274016 Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 3 Feb 2013 13:44:37 +0000 Subject: [PATCH 009/158] Don't handle selections that are not from the inner frame --- src/static/js/ace2_inner.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index da8dea85d..848fc20b0 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -4119,6 +4119,11 @@ function Ace2Inner(){ selection.startPoint = pointFromRangeBound(range.startContainer, range.startOffset); selection.endPoint = pointFromRangeBound(range.endContainer, range.endOffset); selection.focusAtStart = (((range.startContainer != range.endContainer) || (range.startOffset != range.endOffset)) && browserSelection.anchorNode && (browserSelection.anchorNode == range.endContainer) && (browserSelection.anchorOffset == range.endOffset)); + + if(selection.startPoint.node.ownerDocument !== window.document){ + return null; + } + return selection; } else return null; From 8b8cf0178521739b0de1e29da5102f97ca93af27 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sun, 3 Feb 2013 13:53:44 +0000 Subject: [PATCH 010/158] put tests in static folder, still have a race condition no biggy --- src/node/hooks/express/tests.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index 9aeaefe6a..988c3c0c2 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -8,9 +8,9 @@ exports.expressCreateServer = function (hook_name, args, cb) { fs.readdir('node_modules', function(err, plugins){ // installed plugins plugins.forEach(function(plugin){ // for each one - if(fs.existsSync("node_modules/"+plugin+"/tests/frontend/specs")){ // If the folder exists - fs.readdir("node_modules/"+plugin+"/tests/frontend/specs/", function(err, pluginFiles){ - files.push("/static/plugins/"+plugin+"/tests/frontend/specs/"+pluginFiles); + if(fs.existsSync("node_modules/"+plugin+"/static/tests/frontend/specs")){ // If the folder exists + fs.readdir("node_modules/"+plugin+"/static/tests/frontend/specs/", function(err, pluginFiles){ + files.push("/static/plugins/"+plugin+"/static/tests/frontend/specs/"+pluginFiles); }); } }); From cba001341f2b184684931591598838e1c750d86c Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 3 Feb 2013 14:03:10 +0000 Subject: [PATCH 011/158] Remove legacy mozilla specific code from ace inner --- src/static/js/ace2_common.js | 14 -- src/static/js/ace2_inner.js | 334 +---------------------------------- src/static/js/domline.js | 3 +- 3 files changed, 2 insertions(+), 349 deletions(-) diff --git a/src/static/js/ace2_common.js b/src/static/js/ace2_common.js index 8a7d16ee3..fb7ce4feb 100644 --- a/src/static/js/ace2_common.js +++ b/src/static/js/ace2_common.js @@ -33,19 +33,6 @@ function object(o) f.prototype = o; return new f(); } -var userAgent = (((function () {return this;})().navigator || {}).userAgent || 'node-js').toLowerCase(); - -// Figure out what browser is being used (stolen from jquery 1.2.1) -var browser = { - version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1], - safari: /webkit/.test(userAgent), - opera: /opera/.test(userAgent), - msie: /msie/.test(userAgent) && !/opera/.test(userAgent), - mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent), - windows: /windows/.test(userAgent), - mobile: /mobile/.test(userAgent) || /android/.test(userAgent) -}; - function getAssoc(obj, name) { @@ -97,7 +84,6 @@ var noop = function(){}; exports.isNodeText = isNodeText; exports.object = object; -exports.browser = browser; exports.getAssoc = getAssoc; exports.setAssoc = setAssoc; exports.binarySearch = binarySearch; diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 848fc20b0..95ffb0384 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -28,7 +28,7 @@ $ = jQuery = require('./rjquery').$; _ = require("./underscore"); var isNodeText = Ace2Common.isNodeText, - browser = Ace2Common.browser, + browser = $.browser, getAssoc = Ace2Common.getAssoc, setAssoc = Ace2Common.setAssoc, isTextNode = Ace2Common.isTextNode, @@ -2817,7 +2817,6 @@ function Ace2Inner(){ rep.selStart = selectStart; rep.selEnd = selectEnd; rep.selFocusAtStart = newSelFocusAtStart; - if (mozillaFakeArrows) mozillaFakeArrows.notifySelectionChanged(); currentCallStack.repChanged = true; return true; @@ -3690,12 +3689,6 @@ function Ace2Inner(){ doDeleteKey(); specialHandled = true; } - - if (mozillaFakeArrows && mozillaFakeArrows.handleKeyEvent(evt)) - { - evt.preventDefault(); - specialHandled = true; - } } if (type == "keydown") @@ -5037,331 +5030,6 @@ function Ace2Inner(){ } editorInfo.ace_doInsertUnorderedList = doInsertUnorderedList; editorInfo.ace_doInsertOrderedList = doInsertOrderedList; - - var mozillaFakeArrows = (browser.mozilla && (function() - { - // In Firefox 2, arrow keys are unstable while DOM-manipulating - // operations are going on. Specifically, if an operation - // (computation that ties up the event queue) is going on (in the - // call-stack of some event, like a timeout) that at some point - // mutates nodes involved in the selection, then the arrow - // keypress may (randomly) move the caret to the beginning or end - // of the document. If the operation also mutates the selection - // range, the old selection or the new selection may be used, or - // neither. - // As long as the arrow is pressed during the busy operation, it - // doesn't seem to matter that the keydown and keypress events - // aren't generated until afterwards, or that the arrow movement - // can still be stopped (meaning it hasn't been performed yet); - // Firefox must be preserving some old information about the - // selection or the DOM from when the key was initially pressed. - // However, it also doesn't seem to matter when the key was - // actually pressed relative to the time of the mutation within - // the prolonged operation. Also, even in very controlled tests - // (like a mutation followed by a long period of busyWaiting), the - // problem shows up often but not every time, with no discernable - // pattern. Who knows, it could have something to do with the - // caret-blinking timer, or DOM changes not being applied - // immediately. - // This problem, mercifully, does not show up at all in IE or - // Safari. My solution is to have my own, full-featured arrow-key - // implementation for Firefox. - // Note that the problem addressed here is potentially very subtle, - // especially if the operation is quick and is timed to usually happen - // when the user is idle. - // features: - // - 'up' and 'down' arrows preserve column when passing through shorter lines - // - shift-arrows extend the "focus" point, which may be start or end of range - // - the focus point is kept horizontally and vertically scrolled into view - // - arrows without shift cause caret to move to beginning or end of selection (left,right) - // or move focus point up or down a line (up,down) - // - command-(left,right,up,down) on Mac acts like (line-start, line-end, doc-start, doc-end) - // - takes wrapping into account when doesWrap is true, i.e. up-arrow and down-arrow move - // between the virtual lines within a wrapped line; this was difficult, and unfortunately - // requires mutating the DOM to get the necessary information - var savedFocusColumn = 0; // a value of 0 has no effect - var updatingSelectionNow = false; - - function getVirtualLineView(lineNum) - { - var lineNode = rep.lines.atIndex(lineNum).lineNode; - while (lineNode.firstChild && isBlockElement(lineNode.firstChild)) - { - lineNode = lineNode.firstChild; - } - return makeVirtualLineView(lineNode); - } - - function markerlessLineAndChar(line, chr) - { - return [line, chr - rep.lines.atIndex(line).lineMarker]; - } - - function markerfulLineAndChar(line, chr) - { - return [line, chr + rep.lines.atIndex(line).lineMarker]; - } - - return { - notifySelectionChanged: function() - { - if (!updatingSelectionNow) - { - savedFocusColumn = 0; - } - }, - handleKeyEvent: function(evt) - { - // returns "true" if handled - if (evt.type != "keypress") return false; - var keyCode = evt.keyCode; - if (keyCode < 37 || keyCode > 40) return false; - incorporateUserChanges(); - - if (!(rep.selStart && rep.selEnd)) return true; - - // {byWord,toEnd,normal} - var moveMode = (evt.altKey ? "byWord" : (evt.ctrlKey ? "byWord" : (evt.metaKey ? "toEnd" : "normal"))); - - var anchorCaret = markerlessLineAndChar(rep.selStart[0], rep.selStart[1]); - var focusCaret = markerlessLineAndChar(rep.selEnd[0], rep.selEnd[1]); - var wasCaret = isCaret(); - if (rep.selFocusAtStart) - { - var tmp = anchorCaret; - anchorCaret = focusCaret; - focusCaret = tmp; - } - var K_UP = 38, - K_DOWN = 40, - K_LEFT = 37, - K_RIGHT = 39; - var dontMove = false; - if (wasCaret && !evt.shiftKey) - { - // collapse, will mutate both together - anchorCaret = focusCaret; - } - else if ((!wasCaret) && (!evt.shiftKey)) - { - if (keyCode == K_LEFT) - { - // place caret at beginning - if (rep.selFocusAtStart) anchorCaret = focusCaret; - else focusCaret = anchorCaret; - if (moveMode == "normal") dontMove = true; - } - else if (keyCode == K_RIGHT) - { - // place caret at end - if (rep.selFocusAtStart) focusCaret = anchorCaret; - else anchorCaret = focusCaret; - if (moveMode == "normal") dontMove = true; - } - else - { - // collapse, will mutate both together - anchorCaret = focusCaret; - } - } - if (!dontMove) - { - function lineLength(i) - { - var entry = rep.lines.atIndex(i); - return entry.text.length - entry.lineMarker; - } - - function lineText(i) - { - var entry = rep.lines.atIndex(i); - return entry.text.substring(entry.lineMarker); - } - - if (keyCode == K_UP || keyCode == K_DOWN) - { - var up = (keyCode == K_UP); - var canChangeLines = ((up && focusCaret[0]) || ((!up) && focusCaret[0] < rep.lines.length() - 1)); - var virtualLineView, virtualLineSpot, canChangeVirtualLines = false; - if (doesWrap) - { - virtualLineView = getVirtualLineView(focusCaret[0]); - virtualLineSpot = virtualLineView.getVLineAndOffsetForChar(focusCaret[1]); - canChangeVirtualLines = ((up && virtualLineSpot.vline > 0) || ((!up) && virtualLineSpot.vline < ( - virtualLineView.getNumVirtualLines() - 1))); - } - var newColByVirtualLineChange; - if (moveMode == "toEnd") - { - if (up) - { - focusCaret[0] = 0; - focusCaret[1] = 0; - } - else - { - focusCaret[0] = rep.lines.length() - 1; - focusCaret[1] = lineLength(focusCaret[0]); - } - } - else if (moveMode == "byWord") - { - // move by "paragraph", a feature that Firefox lacks but IE and Safari both have - if (up) - { - if (focusCaret[1] === 0 && canChangeLines) - { - focusCaret[0]--; - focusCaret[1] = 0; - } - else focusCaret[1] = 0; - } - else - { - var lineLen = lineLength(focusCaret[0]); - if (browser.windows) - { - if (canChangeLines) - { - focusCaret[0]++; - focusCaret[1] = 0; - } - else - { - focusCaret[1] = lineLen; - } - } - else - { - if (focusCaret[1] == lineLen && canChangeLines) - { - focusCaret[0]++; - focusCaret[1] = lineLength(focusCaret[0]); - } - else - { - focusCaret[1] = lineLen; - } - } - } - savedFocusColumn = 0; - } - else if (canChangeVirtualLines) - { - var vline = virtualLineSpot.vline; - var offset = virtualLineSpot.offset; - if (up) vline--; - else vline++; - if (savedFocusColumn > offset) offset = savedFocusColumn; - else - { - savedFocusColumn = offset; - } - var newSpot = virtualLineView.getCharForVLineAndOffset(vline, offset); - focusCaret[1] = newSpot.lineChar; - } - else if (canChangeLines) - { - if (up) focusCaret[0]--; - else focusCaret[0]++; - var offset = focusCaret[1]; - if (doesWrap) - { - offset = virtualLineSpot.offset; - } - if (savedFocusColumn > offset) offset = savedFocusColumn; - else - { - savedFocusColumn = offset; - } - if (doesWrap) - { - var newLineView = getVirtualLineView(focusCaret[0]); - var vline = (up ? newLineView.getNumVirtualLines() - 1 : 0); - var newSpot = newLineView.getCharForVLineAndOffset(vline, offset); - focusCaret[1] = newSpot.lineChar; - } - else - { - var lineLen = lineLength(focusCaret[0]); - if (offset > lineLen) offset = lineLen; - focusCaret[1] = offset; - } - } - else - { - if (up) focusCaret[1] = 0; - else focusCaret[1] = lineLength(focusCaret[0]); - savedFocusColumn = 0; - } - } - else if (keyCode == K_LEFT || keyCode == K_RIGHT) - { - var left = (keyCode == K_LEFT); - if (left) - { - if (moveMode == "toEnd") focusCaret[1] = 0; - else if (focusCaret[1] > 0) - { - if (moveMode == "byWord") - { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false); - } - else - { - focusCaret[1]--; - } - } - else if (focusCaret[0] > 0) - { - focusCaret[0]--; - focusCaret[1] = lineLength(focusCaret[0]); - if (moveMode == "byWord") - { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], false); - } - } - } - else - { - var lineLen = lineLength(focusCaret[0]); - if (moveMode == "toEnd") focusCaret[1] = lineLen; - else if (focusCaret[1] < lineLen) - { - if (moveMode == "byWord") - { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true); - } - else - { - focusCaret[1]++; - } - } - else if (focusCaret[0] < rep.lines.length() - 1) - { - focusCaret[0]++; - focusCaret[1] = 0; - if (moveMode == "byWord") - { - focusCaret[1] = moveByWordInLine(lineText(focusCaret[0]), focusCaret[1], true); - } - } - } - savedFocusColumn = 0; - } - } - - var newSelFocusAtStart = ((focusCaret[0] < anchorCaret[0]) || (focusCaret[0] == anchorCaret[0] && focusCaret[1] < anchorCaret[1])); - var newSelStart = (newSelFocusAtStart ? focusCaret : anchorCaret); - var newSelEnd = (newSelFocusAtStart ? anchorCaret : focusCaret); - updatingSelectionNow = true; - performSelectionChange(markerfulLineAndChar(newSelStart[0], newSelStart[1]), markerfulLineAndChar(newSelEnd[0], newSelEnd[1]), newSelFocusAtStart); - updatingSelectionNow = false; - currentCallStack.userChangedSelection = true; - return true; - } - }; - })()); var lineNumbersShown; var sideDivInner; diff --git a/src/static/js/domline.js b/src/static/js/domline.js index 1be0f4eee..43b5f21a3 100644 --- a/src/static/js/domline.js +++ b/src/static/js/domline.js @@ -30,8 +30,7 @@ var Security = require('./security'); var hooks = require('./pluginfw/hooks'); var _ = require('./underscore'); var lineAttributeMarker = require('./linestylefilter').lineAttributeMarker; -var Ace2Common = require('./ace2_common'); -var noop = Ace2Common.noop; +var noop = function(){}; var domline = {}; From eaa835630c1219549595226556357cde63706bfb Mon Sep 17 00:00:00 2001 From: Peter 'Pita' Martischka Date: Sun, 3 Feb 2013 17:39:49 +0000 Subject: [PATCH 012/158] Support Page Up and Page Down in ace --- src/static/js/ace2_inner.js | 61 ++++++++++++++++++++++++++----- src/static/js/changesettracker.js | 4 +- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 95ffb0384..d62b9f685 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -154,7 +154,12 @@ function Ace2Inner(){ var dmesg = noop; window.dmesg = noop; - var scheduler = parent; + // Ugly hack for Firefox 18 + // get the timeout and interval methods from the parent iframe + setTimeout = parent.setTimeout; + clearTimeout = parent.clearTimeout; + setInterval = parent.setInterval; + clearInterval = parent.clearInterval; var textFace = 'monospace'; var textSize = 12; @@ -174,7 +179,7 @@ function Ace2Inner(){ parentDynamicCSS = makeCSSManager("dynamicsyntax", true); } - var changesetTracker = makeChangesetTracker(scheduler, rep.apool, { + var changesetTracker = makeChangesetTracker(rep.apool, { withCallbacks: function(operationName, f) { inCallStackIfNecessary(operationName, function() @@ -594,7 +599,7 @@ function Ace2Inner(){ doesWrap = newVal; var dwClass = "doesWrap"; setClassPresence(root, "doesWrap", doesWrap); - scheduler.setTimeout(function() + setTimeout(function() { inCallStackIfNecessary("setWraps", function() { @@ -634,7 +639,7 @@ function Ace2Inner(){ textFace = face; root.style.fontFamily = textFace; lineMetricsDiv.style.fontFamily = textFace; - scheduler.setTimeout(function() + setTimeout(function() { setUpTrackingCSS(); }, 0); @@ -647,7 +652,7 @@ function Ace2Inner(){ root.style.lineHeight = textLineHeight() + "px"; sideDiv.style.lineHeight = textLineHeight() + "px"; lineMetricsDiv.style.fontSize = textSize + "px"; - scheduler.setTimeout(function() + setTimeout(function() { setUpTrackingCSS(); }, 0); @@ -1085,7 +1090,7 @@ function Ace2Inner(){ { if (scheduledTimeout) { - scheduler.clearTimeout(scheduledTimeout); + clearTimeout(scheduledTimeout); scheduledTimeout = null; } } @@ -1096,7 +1101,7 @@ function Ace2Inner(){ scheduledTime = time; var delay = time - now(); if (delay < 0) delay = 0; - scheduledTimeout = scheduler.setTimeout(callback, delay); + scheduledTimeout = setTimeout(callback, delay); } function callback() @@ -3613,7 +3618,7 @@ function Ace2Inner(){ evt.preventDefault(); doReturnKey(); //scrollSelectionIntoView(); - scheduler.setTimeout(function() + setTimeout(function() { outerWin.scrollBy(-100, 0); }, 0); @@ -3689,6 +3694,42 @@ function Ace2Inner(){ doDeleteKey(); specialHandled = true; } + if((evt.which == 33 || evt.which == 34) && type == 'keydown'){ + var oldVisibleLineRange = getVisibleLineRange(); + var topOffset = rep.selStart[0] - oldVisibleLineRange[0]; + if(topOffset < 0 ){ + topOffset = 0; + } + + var isPageDown = evt.which === 34; + var isPageUp = evt.which === 33; + + setTimeout(function(){ + var newVisibleLineRange = getVisibleLineRange(); + var linesCount = rep.lines.length(); + + var newCaretRow = rep.selStart[0]; + if(isPageUp){ + newCaretRow = oldVisibleLineRange[0]; + } + + if(isPageDown){ + newCaretRow = newVisibleLineRange[0] + topOffset; + } + + //ensure min and max + if(newCaretRow < 0){ + newCaretRow = 0; + } + if(newCaretRow >= linesCount){ + newCaretRow = linesCount-1; + } + + rep.selStart[0] = newCaretRow; + rep.selEnd[0] = newCaretRow; + updateBrowserSelectionFromRep(); + }, 200); + } } if (type == "keydown") @@ -4720,7 +4761,7 @@ function Ace2Inner(){ }); - scheduler.setTimeout(function() + setTimeout(function() { parent.readyFunc(); // defined in code that sets up the inner iframe }, 0); @@ -5168,7 +5209,7 @@ function Ace2Inner(){ documentAttributeManager: documentAttributeManager }); - scheduler.setTimeout(function() + setTimeout(function() { parent.readyFunc(); // defined in code that sets up the inner iframe }, 0); diff --git a/src/static/js/changesettracker.js b/src/static/js/changesettracker.js index 58ef21cb5..91e854d65 100644 --- a/src/static/js/changesettracker.js +++ b/src/static/js/changesettracker.js @@ -23,7 +23,7 @@ var AttributePool = require('./AttributePool'); var Changeset = require('./Changeset'); -function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) +function makeChangesetTracker(apool, aceCallbacksProvider) { // latest official text from server @@ -51,7 +51,7 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) // and if there isn't a timeout already scheduled. if (changeCallback && changeCallbackTimeout === null) { - changeCallbackTimeout = scheduler.setTimeout(function() + changeCallbackTimeout = setTimeout(function() { try { From da0b331502a8aac5e9bf12ff0479668f1e605cb2 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 4 Feb 2013 00:00:39 +0000 Subject: [PATCH 013/158] Make async and cleaner --- src/node/hooks/express/tests.js | 56 ++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/node/hooks/express/tests.js b/src/node/hooks/express/tests.js index 988c3c0c2..3157d68ed 100644 --- a/src/node/hooks/express/tests.js +++ b/src/node/hooks/express/tests.js @@ -1,25 +1,26 @@ var path = require("path") , npm = require("npm") - , fs = require("fs"); + , fs = require("fs") + , async = require("async"); exports.expressCreateServer = function (hook_name, args, cb) { args.app.get('/tests/frontend/specs_list.js', function(req, res){ - fs.readdir('tests/frontend/specs', function(err, files){ - fs.readdir('node_modules', function(err, plugins){ // installed plugins - plugins.forEach(function(plugin){ // for each one - if(fs.existsSync("node_modules/"+plugin+"/static/tests/frontend/specs")){ // If the folder exists - fs.readdir("node_modules/"+plugin+"/static/tests/frontend/specs/", function(err, pluginFiles){ - files.push("/static/plugins/"+plugin+"/static/tests/frontend/specs/"+pluginFiles); - }); - } - }); - }); - - if(err){ return res.send(500); } - - res.send("var specs_list = " + JSON.stringify(files.sort()) + ";\n"); + async.parallel({ + coreSpecs: function(callback){ + exports.getCoreTests(callback); + }, + pluginSpecs: function(callback){ + exports.getPluginTests(callback); + } + }, + function(err, results){ + var files = results.coreSpecs; // push the core specs to a file object + files = files.concat(results.pluginSpecs); // add the plugin Specs to the core specs + console.debug("Sent browser the following test specs:", files.sort()); + res.send("var specs_list = " + JSON.stringify(files.sort()) + ";\n"); }); + }); var url2FilePath = function(url){ @@ -56,3 +57,28 @@ exports.expressCreateServer = function (hook_name, args, cb) { res.redirect('/tests/frontend/'); }); } + +exports.getPluginTests = function(callback){ + var pluginSpecs = []; + var plugins = fs.readdirSync('node_modules'); + plugins.forEach(function(plugin){ + if(fs.existsSync("node_modules/"+plugin+"/static/tests/frontend/specs")){ // if plugins exists + var specFiles = fs.readdirSync("node_modules/"+plugin+"/static/tests/frontend/specs/"); + async.forEach(specFiles, function(spec){ // for each specFile push it to pluginSpecs + pluginSpecs.push("/static/plugins/"+plugin+"/static/tests/frontend/specs/" + spec); + }, + function(err){ + // blow up if something bad happens! + }); + } + }); + callback(null, pluginSpecs); +} + +exports.getCoreTests = function(callback){ + fs.readdir('tests/frontend/specs', function(err, coreSpecs){ // get the core test specs + if(err){ return res.send(500); } + callback(null, coreSpecs); + }); +} + From de07d5a3e1a470ccb1f8be8ec76744a5e79f56b1 Mon Sep 17 00:00:00 2001 From: John McLear Date: Mon, 4 Feb 2013 00:03:25 +0000 Subject: [PATCH 014/158] make plugin specs finally work --- tests/frontend/runner.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/frontend/runner.js b/tests/frontend/runner.js index 1679664bf..55f0a45bd 100644 --- a/tests/frontend/runner.js +++ b/tests/frontend/runner.js @@ -179,7 +179,11 @@ $(function(){ //inject spec scripts into the dom var $body = $('body'); $.each(specs, function(i, spec){ - $body.append('') + if(spec[0] != "/"){ // if the spec isn't a plugin spec which means the spec file might be in a different subfolder + $body.append('') + }else{ + $body.append('') + } }); //initalize the test helper @@ -196,4 +200,4 @@ $(function(){ mocha.run(); }); -}); \ No newline at end of file +}); From 718421a46db198aa60ad4b783fb1dbf89b5c85a7 Mon Sep 17 00:00:00 2001 From: Jordan Hollinger Date: Mon, 4 Feb 2013 00:57:51 -0500 Subject: [PATCH 015/158] Bugfixes to getChatHistory API method --- src/node/db/API.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node/db/API.js b/src/node/db/API.js index f99a43afd..07141fec2 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -325,17 +325,17 @@ exports.getChatHistory = function(padID, start, end, callback) if(!start || !end) { start = 0; - end = pad.chatHead - 1; + end = pad.chatHead; } - if(start >= chatHead) + if(start >= chatHead && chatHead > 0) { callback(new customError("start is higher or equal to the current chatHead","apierror")); return; } - if(end >= chatHead) + if(end > chatHead) { - callback(new customError("end is higher or equal to the current chatHead","apierror")); + callback(new customError("end is higher than the current chatHead","apierror")); return; } From 34f9f1515085930d61279402092d23f3d5844305 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 5 Feb 2013 23:33:44 +0400 Subject: [PATCH 016/158] socket.io rooms instead of own session management Replaces homegrown pad session management with socket.io build-in rooms --- src/node/handler/PadMessageHandler.js | 432 ++++++++++---------------- 1 file changed, 163 insertions(+), 269 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 6781cd884..69bb568db 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -35,11 +35,6 @@ var messageLogger = log4js.getLogger("message"); var _ = require('underscore'); var hooks = require("ep_etherpad-lite/static/js/pluginfw/hooks.js"); -/** - * A associative array that saves which sessions belong to a pad - */ -var pad2sessions = {}; - /** * A associative array that saves informations about a session * key = sessionId @@ -83,14 +78,11 @@ exports.handleConnect = function(client) exports.kickSessionsFromPad = function(padID) { //skip if there is nobody on this pad - if(!pad2sessions[padID]) + if(socketio.sockets.clients(padID).length == 0) return; //disconnect everyone from this pad - for(var i in pad2sessions[padID]) - { - socketio.sockets.sockets[pad2sessions[padID][i]].json.send({disconnect:"deleted"}); - } + socketio.sockets.in(padID).json.send({disconnect:"deleted"}); } /** @@ -100,15 +92,13 @@ exports.kickSessionsFromPad = function(padID) exports.handleDisconnect = function(client) { //save the padname of this session - var sessionPad=sessioninfos[client.id].padId; + var session = sessioninfos[client.id]; //if this connection was already etablished with a handshake, send a disconnect message to the others - if(sessioninfos[client.id] && sessioninfos[client.id].author) + if(session && session.author) { - var author = sessioninfos[client.id].author; - //get the author color out of the db - authorManager.getAuthorColorId(author, function(err, color) + authorManager.getAuthorColorId(session.author, function(err, color) { ERR(err); @@ -121,33 +111,16 @@ exports.handleDisconnect = function(client) "ip": "127.0.0.1", "colorId": color, "userAgent": "Anonymous", - "userId": author + "userId": session.author } } }; //Go trough all user that are still on the pad, and send them the USER_LEAVE message - for(i in pad2sessions[sessionPad]) - { - var socket = socketio.sockets.sockets[pad2sessions[sessionPad][i]]; - if(socket !== undefined){ - socket.json.send(messageToTheOtherUsers); - } - - } + client.broadcast.to(session.padId).json.send(messageToTheOtherUsers); }); } - //Go trough all sessions of this pad, search and destroy the entry of this client - for(i in pad2sessions[sessionPad]) - { - if(pad2sessions[sessionPad][i] == client.id) - { - pad2sessions[sessionPad].splice(i, 1); - break; - } - } - //Delete the sessioninfos entrys of this session delete sessioninfos[client.id]; } @@ -228,11 +201,10 @@ exports.handleMessage = function(client, message) function(callback) { - if(!message.padId){ - // If the message has a padId we assume the client is already known to the server and needs no re-authorization - callback(); - return; - } + // If the message has a padId we assume the client is already known to the server and needs no re-authorization + if(!message.padId) + return callback(); + // Note: message.sessionID is an entirely different kind of // session from the sessions we use here! Beware! FIXME: Call // our "sessions" "connections". @@ -292,9 +264,7 @@ exports.handleCustomMessage = function (padID, msg, cb) { time: time } }; - for (var i in pad2sessions[padID]) { - socketio.sockets.sockets[pad2sessions[padID][i]].json.send(msg); - } + socketio.sockets.in(padID).json.send(msg); cb(null, {}); } @@ -352,10 +322,7 @@ function handleChatMessage(client, message) }; //broadcast the chat message to everyone on the pad - for(var i in pad2sessions[padId]) - { - socketio.sockets.sockets[pad2sessions[padId][i]].json.send(msg); - } + socketio.sockets.in(padId).json.send(msg); callback(); } @@ -413,23 +380,16 @@ function handleGetChatMessages(client, message) { if(ERR(err, callback)) return; - var infoMsg = { - type: "COLLABROOM", - data: { - type: "CHAT_MESSAGES", - messages: chatMessages - } - }; - - // send the messages back to the client - for(var i in pad2sessions[padId]) - { - if(pad2sessions[padId][i] == client.id) - { - socketio.sockets.sockets[pad2sessions[padId][i]].json.send(infoMsg); - break; + var infoMsg = { + type: "COLLABROOM", + data: { + type: "CHAT_MESSAGES", + messages: chatMessages } - } + }; + + // send the messages back to the client + client.json.send(infoMsg); }); }]); } @@ -453,14 +413,14 @@ function handleSuggestUserName(client, message) return; } - var padId = sessioninfos[client.id].padId; + var padId = sessioninfos[client.id].padId, + clients = socketio.sockets.clients(padId); //search the author and send him this message - for(var i in pad2sessions[padId]) - { - if(sessioninfos[pad2sessions[padId][i]].author == message.data.payload.unnamedId) - { - socketio.sockets.sockets[pad2sessions[padId][i]].send(message); + for(var i = 0; i < clients.length; i++) { + var session = sessioninfos[clients[i].id]; + if(session && session.author == message.data.payload.unnamedId) { + clients[i].json.send(message); break; } } @@ -501,7 +461,8 @@ function handleUserInfoUpdate(client, message) type: "USER_NEWINFO", userInfo: { userId: author, - name: message.data.userInfo.name, + //set a null name, when there is no name set. cause the client wants it null + name: message.data.userInfo.name || null, colorId: message.data.userInfo.colorId, userAgent: "Anonymous", ip: "127.0.0.1", @@ -509,20 +470,8 @@ function handleUserInfoUpdate(client, message) } }; - //set a null name, when there is no name set. cause the client wants it null - if(infoMsg.data.userInfo.name == null) - { - infoMsg.data.userInfo.name = null; - } - //Send the other clients on the pad the update message - for(var i in pad2sessions[padId]) - { - if(pad2sessions[padId][i] != client.id) - { - socketio.sockets.sockets[pad2sessions[padId][i]].json.send(infoMsg); - } - } + client.broadcast.to(padId).json.send(infoMsg); } /** @@ -682,90 +631,76 @@ function handleUserChanges(client, message) exports.updatePadClients = function(pad, callback) { //skip this step if noone is on this pad - if(!pad2sessions[pad.id]) - { - callback(); - return; - } + var roomClients = socketio.sockets.clients(pad.id); + if(roomClients.length==0) + return callback(); + // since all clients usually get the same set of changesets, store them in local cache + // to remove unnecessary roundtrip to the datalayer + // TODO: in REAL world, if we're working without datalayer cache, all requests to revisions will be fired + // BEFORE first result will be landed to our cache object. The solution is to replace parallel processing + // via async.forEach with sequential for() loop. There is no real benefits of running this in parallel, + // but benefit of reusing cached revision object is HUGE + var revCache = {}; + //go trough all sessions on this pad - async.forEach(pad2sessions[pad.id], function(session, callback) + async.forEach(roomClients, function(client, callback) { + var sid = client.id; //https://github.com/caolan/async#whilst //send them all new changesets async.whilst( - function (){ return sessioninfos[session] && sessioninfos[session].rev < pad.getHeadRevisionNumber()}, + function (){ return sessioninfos[sid] && sessioninfos[sid].rev < pad.getHeadRevisionNumber()}, function(callback) { - var author, revChangeset, currentTime; - var r = sessioninfos[session].rev + 1; - - async.parallel([ - function (callback) - { - pad.getRevisionAuthor(r, function(err, value) - { - if(ERR(err, callback)) return; - author = value; - callback(); - }); - }, - function (callback) - { - pad.getRevisionChangeset(r, function(err, value) - { - if(ERR(err, callback)) return; - revChangeset = value; - callback(); - }); - }, - function (callback) - { - pad.getRevisionDate(r, function(err, date) - { - if(ERR(err, callback)) return; - currentTime = date; - callback(); - }); - } - ], function(err) - { - if(ERR(err, callback)) return; - // next if session has not been deleted - if(sessioninfos[session] == null) - { - callback(null); - return; - } - if(author == sessioninfos[session].author) - { - socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); - } - else - { - var forWire = Changeset.prepareForWire(revChangeset, pad.pool); - var wireMsg = {"type":"COLLABROOM", - "data":{type:"NEW_CHANGES", - newRev:r, - changeset: forWire.translated, - apool: forWire.pool, - author: author, - currentTime: currentTime, - timeDelta: currentTime - sessioninfos[session].time - }}; - - socketio.sockets.sockets[session].json.send(wireMsg); - } + var r = sessioninfos[sid].rev + 1; - if(sessioninfos[session] != null) - { - sessioninfos[session].time = currentTime; - sessioninfos[session].rev = r; - } - - callback(null); - }); + async.waterfall([ + function(callback) { + if(revCache[r]) + callback(null, revCache[r]); + else + pad.getRevision(r, callback); + }, + function(revision, callback) + { + revCache[r] = revision; + + var author = revision.meta.author, + revChangeset = revision.changeset, + currentTime = revision.meta.timestamp; + + // next if session has not been deleted + if(sessioninfos[sid] == null) + return callback(null); + + if(author == sessioninfos[sid].author) + { + client.json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); + } + else + { + var forWire = Changeset.prepareForWire(revChangeset, pad.pool); + var wireMsg = {"type":"COLLABROOM", + "data":{type:"NEW_CHANGES", + newRev:r, + changeset: forWire.translated, + apool: forWire.pool, + author: author, + currentTime: currentTime, + timeDelta: currentTime - sessioninfos[sid].time + }}; + + client.json.send(wireMsg); + } + + sessioninfos[sid].time = currentTime; + sessioninfos[sid].rev = r; + + callback(null); + } + ], callback); }, callback ); @@ -895,23 +830,14 @@ function handleClientReady(client, message) function(callback) { async.parallel([ - //get colorId + //get colorId and name function(callback) { - authorManager.getAuthorColorId(author, function(err, value) + authorManager.getAuthor(author, function(err, value) { if(ERR(err, callback)) return; - authorColorId = value; - callback(); - }); - }, - //get author name - function(callback) - { - authorManager.getAuthorName(author, function(err, value) - { - if(ERR(err, callback)) return; - authorName = value; + authorColorId = value.colorId; + authorName = value.name; callback(); }); }, @@ -965,21 +891,17 @@ function handleClientReady(client, message) { //Check that the client is still here. It might have disconnected between callbacks. if(sessioninfos[client.id] === undefined) - { - callback(); - return; - } + return callback(); //Check if this author is already on the pad, if yes, kick the other sessions! - if(pad2sessions[padIds.padId]) - { - for(var i in pad2sessions[padIds.padId]) - { - if(sessioninfos[pad2sessions[padIds.padId][i]] && sessioninfos[pad2sessions[padIds.padId][i]].author == author) - { - var socket = socketio.sockets.sockets[pad2sessions[padIds.padId][i]]; - if(socket) socket.json.send({disconnect:"userdup"}); - } + var roomClients = socketio.sockets.clients(padIds.padId); + for(var i = 0; i < roomClients.length; i++) { + var sinfo = sessioninfos[roomClients[i].id]; + if(sinfo && sinfo.author == author) { + // fix user's counter, works on page refresh or if user closes browser window and then rejoins + sessioninfos[roomClients[i].id] = {}; + roomClients[i].leave(padIds.padId); + roomClients[i].json.send({disconnect:"userdup"}); } } @@ -988,15 +910,6 @@ function handleClientReady(client, message) sessioninfos[client.id].readOnlyPadId = padIds.readOnlyPadId; sessioninfos[client.id].readonly = padIds.readonly; - //check if there is already a pad2sessions entry, if not, create one - if(!pad2sessions[padIds.padId]) - { - pad2sessions[padIds.padId] = []; - } - - //Saves in pad2sessions that this session belongs to this pad - pad2sessions[padIds.padId].push(client.id); - //If this is a reconnect, we don't have to send the client the ClientVars again if(message.reconnect == true) { @@ -1044,7 +957,7 @@ function handleClientReady(client, message) // tell the client the number of the latest chat-message, which will be // used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES) "chatHead": pad.chatHead, - "numConnectedUsers": pad2sessions[padIds.padId].length, + "numConnectedUsers": roomClients.length, "isProPad": false, "readOnlyId": padIds.readOnlyPadId, "readonly": padIds.readonly, @@ -1080,6 +993,8 @@ function handleClientReady(client, message) } }); + //Join the pad and start receiving updates + client.join(padIds.padId); //Send the clientVars to the Client client.json.send({type: "CLIENT_VARS", data: clientVars}); //Save the current revision in sessioninfos, should be the same as in clientVars @@ -1108,74 +1023,56 @@ function handleClientReady(client, message) { messageToTheOtherUsers.data.userInfo.name = authorName; } + + // notify all existing users about new user + client.broadcast.to(padIds.padIds).json.send(messageToTheOtherUsers); //Run trough all sessions of this pad - async.forEach(pad2sessions[padIds.padId], function(sessionID, callback) + async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback) { - var author, socket, sessionAuthorName, sessionAuthorColorId; + var author; + + //Jump over, if this session is the connection session + if(roomClient.id == client.id) + return callback(); + //Since sessioninfos might change while being enumerated, check if the //sessionID is still assigned to a valid session - if(sessioninfos[sessionID] !== undefined && - socketio.sockets.sockets[sessionID] !== undefined){ - author = sessioninfos[sessionID].author; - socket = socketio.sockets.sockets[sessionID]; - }else { - // If the sessionID is not valid, callback(); - callback(); - return; - } - async.series([ + if(sessioninfos[roomClient.id] !== undefined) + author = sessioninfos[roomClient.id].author; + else // If the client id is not valid, callback(); + return callback(); + + async.waterfall([ //get the authorname & colorId function(callback) { - async.parallel([ - function(callback) - { - authorManager.getAuthorColorId(author, function(err, value) - { - if(ERR(err, callback)) return; - sessionAuthorColorId = value; - callback(); - }) - }, - function(callback) - { - authorManager.getAuthorName(author, function(err, value) - { - if(ERR(err, callback)) return; - sessionAuthorName = value; - callback(); - }) - } - ],callback); + // reuse previously created cache of author's data + if(historicalAuthorData[author]) + callback(null, historicalAuthorData[author]); + else + authorManager.getAuthor(author, callback); }, - function (callback) + function (authorInfo, callback) { - //Jump over, if this session is the connection session - if(sessionID != client.id) - { - //Send this Session the Notification about the new user - socket.json.send(messageToTheOtherUsers); - - //Send the new User a Notification about this other user - var messageToNotifyTheClientAboutTheOthers = { - "type": "COLLABROOM", - "data": { - type: "USER_NEWINFO", - userInfo: { - "ip": "127.0.0.1", - "colorId": sessionAuthorColorId, - "name": sessionAuthorName, - "userAgent": "Anonymous", - "userId": author - } + //Send the new User a Notification about this other user + var msg = { + "type": "COLLABROOM", + "data": { + type: "USER_NEWINFO", + userInfo: { + "ip": "127.0.0.1", + "colorId": authorInfo.colorId, + "name": authorInfo.name, + "userAgent": "Anonymous", + "userId": author } - }; - client.json.send(messageToNotifyTheClientAboutTheOthers); - } + } + }; + client.json.send(msg); } - ], callback); + ], callback); }, callback); } ],function(err) @@ -1521,33 +1418,30 @@ function composePadChangesets(padId, startNum, endNum, callback) * Get the number of users in a pad */ exports.padUsersCount = function (padID, callback) { - if (!pad2sessions[padID] || typeof pad2sessions[padID] != typeof []) { - callback(null, {padUsersCount: 0}); - } else { - callback(null, {padUsersCount: pad2sessions[padID].length}); - } + callback(null, { + padUsersCount: socketio.sockets.clients(padId).length + }); } /** * Get the list of users in a pad */ exports.padUsers = function (padID, callback) { - if (!pad2sessions[padID] || typeof pad2sessions[padID] != typeof []) { - callback(null, {padUsers: []}); - } else { - var authors = []; - for ( var ix in sessioninfos ) { - if ( sessioninfos[ix].padId !== padID ) { - continue; - } - var aid = sessioninfos[ix].author; - authorManager.getAuthor( aid, function ( err, author ) { - author.id = aid; - authors.push( author ); - if ( authors.length === pad2sessions[padID].length ) { - callback(null, {padUsers: authors}); - } - } ); + var result = []; + + async.forEach(socketio.sockets.clients(padId), function(roomClient, callback) { + var s = sessioninfos[roomClient.id]; + if(s) { + authorManager.getAuthor(s.author, function(err, author) { + if(ERR(err, callback)) return; + + author.id = s.author; + result.push(author); + }); } - } + }, function(err) { + if(ERR(err, callback)) return; + + callback(null, {padUsers: result}); + }); } From dd49c71af998ad1d89a2cad7c52bbf1821b9ffaf Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 6 Feb 2013 19:43:28 +0000 Subject: [PATCH 017/158] fixes IE8 error by only redifining if firefox --- src/static/js/ace2_inner.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index d62b9f685..8c4beef5c 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -156,10 +156,13 @@ function Ace2Inner(){ // Ugly hack for Firefox 18 // get the timeout and interval methods from the parent iframe - setTimeout = parent.setTimeout; - clearTimeout = parent.clearTimeout; - setInterval = parent.setInterval; - clearInterval = parent.clearInterval; + var FIREFOX = /Firefox/i.test(navigator.userAgent); + if(FIREFOX){ + setTimeout = parent.setTimeout; + clearTimeout = parent.clearTimeout; + setInterval = parent.setInterval; + clearInterval = parent.clearInterval; + } var textFace = 'monospace'; var textSize = 12; From 9fcabfa8ac08cb9e8147998aecf80754d78093d9 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 6 Feb 2013 20:02:24 +0000 Subject: [PATCH 018/158] fix IE8 styling y u no do this 0ip? --- src/templates/index.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/templates/index.html b/src/templates/index.html index c3c13db32..4f6d500b9 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -64,7 +64,8 @@ box-shadow: 0px 1px 8px rgba(0,0,0,0.3); } #inner { - width: 300px; + position:relative; + max-width: 300px; margin: 0 auto; } #button { @@ -100,6 +101,10 @@ text-shadow: 0 1px 1px #fff; margin: 16px auto 0; } + #padname{ + height:38px; + max-width:280px; + } form { height: 38px; background: #fff; @@ -115,7 +120,8 @@ border-radius: 3px; box-sizing: border-box; -moz-box-sizing: border-box; - padding: 0 45px 0 10px; + line-height:36px; /* IE8 hack */ + padding: 0px 45px 0 10px; *padding: 0; /* IE7 hack */ width: 100%; height: 100%; @@ -125,7 +131,7 @@ } button[type="submit"] { position: absolute; - right: 0; + left:253px; width: 45px; height: 38px; } From adaded343dd9b58fa8912dbe8ccf162b6907c12e Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 6 Feb 2013 20:09:30 +0000 Subject: [PATCH 019/158] better fix for IE8 page up down --- src/static/js/ace2_inner.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 8c4beef5c..6d8c58bbc 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -156,12 +156,14 @@ function Ace2Inner(){ // Ugly hack for Firefox 18 // get the timeout and interval methods from the parent iframe - var FIREFOX = /Firefox/i.test(navigator.userAgent); - if(FIREFOX){ + // This hack breaks IE8 + try{ setTimeout = parent.setTimeout; clearTimeout = parent.clearTimeout; setInterval = parent.setInterval; clearInterval = parent.clearInterval; + }catch(err){ + // IE8 can panic here. } var textFace = 'monospace'; From e4032f4d7438403c89fe7147b9fc7daca5f80d88 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 6 Feb 2013 23:28:46 +0000 Subject: [PATCH 020/158] timeslider label tests --- tests/frontend/specs/timeslider_labels.js | 64 +++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/frontend/specs/timeslider_labels.js diff --git a/tests/frontend/specs/timeslider_labels.js b/tests/frontend/specs/timeslider_labels.js new file mode 100644 index 000000000..d39685efe --- /dev/null +++ b/tests/frontend/specs/timeslider_labels.js @@ -0,0 +1,64 @@ +describe("timeslider", function(){ + //create a new pad before each test run + beforeEach(function(cb){ + helper.newPad(cb); + this.timeout(60000); + }); + + it("Shows a date and time in the timeslider and make sure it doesn't include NaN", function(done) { + var inner$ = helper.padInner$; + var chrome$ = helper.padChrome$; + + // make some changes to produce 100 revisions + var revs = 10; + this.timeout(60000); + for(var i=0; i < revs; i++) { + setTimeout(function() { + // enter 'a' in the first text element + inner$("div").first().sendkeys('a'); + }, 200); + } + + setTimeout(function() { + // go to timeslider + $('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider'); + + setTimeout(function() { + var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; + var $sliderBar = timeslider$('#ui-slider-bar'); + + var latestContents = timeslider$('#padcontent').text(); + + // Expect the date and time to be shown + + // Click somewhere on the timeslider + var e = new jQuery.Event('mousedown'); + e.clientX = e.pageX = 150; + e.clientY = e.pageY = 45; + $sliderBar.trigger(e); + + e = new jQuery.Event('mousedown'); + e.clientX = e.pageX = 150; + e.clientY = e.pageY = 40; + $sliderBar.trigger(e); + + e = new jQuery.Event('mousedown'); + e.clientX = e.pageX = 150; + e.clientY = e.pageY = 50; + $sliderBar.trigger(e); + + $sliderBar.trigger('mouseup') + + setTimeout(function() { + //make sure the text has changed + expect( timeslider$('#timer').text() ).not.to.eql( "" ); + expect( timeslider$('#revision_date').text() ).not.to.eql( "" ); + expect( timeslider$('#revision_label').text() ).not.to.eql( "" ); + var includesNaN = timeslider$('#revision_label').text().indexOf("NaN"); // NaN is bad. Naan ist gut + expect( includesNaN ).to.eql( -1 ); // not quite so tasty, I like curry. + done(); + }, 400); + }, 2000); + }, 2000); + }); +}); From 284748adb7fae0e7fa246cff9e90623be518a719 Mon Sep 17 00:00:00 2001 From: John McLear Date: Wed, 6 Feb 2013 23:35:58 +0000 Subject: [PATCH 021/158] stop slider showing NaN on tiny pads --- src/static/js/broadcast_slider.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 08ac08b5a..5acaedaff 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -108,11 +108,15 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) newpos = Number(newpos); if (newpos < 0 || newpos > sliderLength) return; window.location.hash = "#" + newpos; + if(!newpos){ + newpos = 0; // stops it from displaying NaN if newpos isn't set + } $("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)); $("a.tlink").map(function() { $(this).attr('href', $(this).attr('thref').replace("%revision%", newpos)); }); + console.log(newpos); $("#revision_label").html(html10n.get("timeslider.version", { "version": newpos})); if (newpos == 0) From 6e8dcee6501b6d05603979743c18b93a9782b6e2 Mon Sep 17 00:00:00 2001 From: mluto Date: Thu, 7 Feb 2013 11:15:16 +0100 Subject: [PATCH 022/158] Removed old chat-code --- src/static/js/pad.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 27dd3b737..43ad7aab4 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -501,7 +501,6 @@ var pad = { padeditor.init(postAceInit, pad.padOptions.view || {}, this); paduserlist.init(pad.myUserInfo, this); - // padchat.init(clientVars.chatHistory, pad.myUserInfo); padconnectionstatus.init(); padmodals.init(this); @@ -540,7 +539,7 @@ var pad = { $('#options-stickychat').prop("checked", true); // set the checkbox to on } if(padcookie.getPref("showAuthorshipColors") == false){ - pad.changeViewOption('showAuthorColors', false); + pad.changeViewOption('showAuthorColors', false); } hooks.aCallAll("postAceInit", {ace: padeditor.ace}); } @@ -553,13 +552,11 @@ var pad = { { pad.myUserInfo.name = newName; pad.collabClient.updateUserInfo(pad.myUserInfo); - //padchat.handleUserJoinOrUpdate(pad.myUserInfo); }, notifyChangeColor: function(newColorId) { pad.myUserInfo.colorId = newColorId; pad.collabClient.updateUserInfo(pad.myUserInfo); - //padchat.handleUserJoinOrUpdate(pad.myUserInfo); }, notifyChangeTitle: function(newTitle) { @@ -643,17 +640,14 @@ var pad = { handleUserJoin: function(userInfo) { paduserlist.userJoinOrUpdate(userInfo); - //padchat.handleUserJoinOrUpdate(userInfo); }, handleUserUpdate: function(userInfo) { paduserlist.userJoinOrUpdate(userInfo); - //padchat.handleUserJoinOrUpdate(userInfo); }, handleUserLeave: function(userInfo) { paduserlist.userLeave(userInfo); - //padchat.handleUserLeave(userInfo); }, handleClientMessage: function(msg) { @@ -665,10 +659,6 @@ var pad = { paduserlist.setMyUserInfo(pad.myUserInfo); } } - else if (msg.type == 'chat') - { - //padchat.receiveChat(msg); - } else if (msg.type == 'padtitle') { paddocbar.changeTitle(msg.title); From 0cbd4034b15dda8e9391eda66bb9c6bfbb46d063 Mon Sep 17 00:00:00 2001 From: mluto Date: Thu, 7 Feb 2013 13:59:41 +0100 Subject: [PATCH 023/158] Killed pad_docbar.js as it's from the old etherpad and not used --- src/static/js/pad.js | 36 --- src/static/js/pad_docbar.js | 466 ------------------------------------ src/static/js/pad_impexp.js | 9 - 3 files changed, 511 deletions(-) delete mode 100644 src/static/js/pad_docbar.js diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 43ad7aab4..8b8d16be7 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -35,7 +35,6 @@ var chat = require('./chat').chat; var getCollabClient = require('./collab_client').getCollabClient; var padconnectionstatus = require('./pad_connectionstatus').padconnectionstatus; var padcookie = require('./pad_cookie').padcookie; -var paddocbar = require('./pad_docbar').paddocbar; var padeditbar = require('./pad_editbar').padeditbar; var padeditor = require('./pad_editor').padeditor; var padimpexp = require('./pad_impexp').padimpexp; @@ -488,13 +487,6 @@ var pad = { $("#specialkeyarea").html("mode: " + String(clientVars.specialKeyTranslation).toUpperCase()); } } - paddocbar.init( - { - isTitleEditable: pad.getIsProPad(), - initialTitle: clientVars.initialTitle, - initialPassword: clientVars.initialPassword, - guestPolicy: pad.padOptions.guestPolicy - }, this); padimpexp.init(this); padsavedrevs.init(this); @@ -558,24 +550,6 @@ var pad = { pad.myUserInfo.colorId = newColorId; pad.collabClient.updateUserInfo(pad.myUserInfo); }, - notifyChangeTitle: function(newTitle) - { - pad.collabClient.sendClientMessage( - { - type: 'padtitle', - title: newTitle, - changedBy: pad.myUserInfo.name || "unnamed" - }); - }, - notifyChangePassword: function(newPass) - { - pad.collabClient.sendClientMessage( - { - type: 'padpassword', - password: newPass, - changedBy: pad.myUserInfo.name || "unnamed" - }); - }, changePadOption: function(key, value) { var options = {}; @@ -616,7 +590,6 @@ var pad = { { // order important here pad.padOptions.guestPolicy = opts.guestPolicy; - paddocbar.setGuestPolicy(opts.guestPolicy); } }, getPadOptions: function() @@ -659,14 +632,6 @@ var pad = { paduserlist.setMyUserInfo(pad.myUserInfo); } } - else if (msg.type == 'padtitle') - { - paddocbar.changeTitle(msg.title); - } - else if (msg.type == 'padpassword') - { - paddocbar.changePassword(msg.password); - } else if (msg.type == 'newRevisionList') { padsavedrevs.newRevisionList(msg.revisionList); @@ -759,7 +724,6 @@ var pad = { } padeditor.disable(); padeditbar.disable(); - paddocbar.disable(); padimpexp.disable(); padconnectionstatus.disconnected(message); diff --git a/src/static/js/pad_docbar.js b/src/static/js/pad_docbar.js deleted file mode 100644 index c58581267..000000000 --- a/src/static/js/pad_docbar.js +++ /dev/null @@ -1,466 +0,0 @@ -/** - * This code is mostly from the old Etherpad. Please help us to comment this code. - * This helps other people to understand this code better and helps them to improve it. - * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED - */ - -/** - * Copyright 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS-IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -var padutils = require('./pad_utils').padutils; - -var paddocbar = (function() -{ - var isTitleEditable = false; - var isEditingTitle = false; - var isEditingPassword = false; - var enabled = false; - - function getPanelOpenCloseAnimator(panelName, panelHeight) - { - var wrapper = $("#" + panelName + "-wrapper"); - var openingClass = "docbar" + panelName + "-opening"; - var openClass = "docbar" + panelName + "-open"; - var closingClass = "docbar" + panelName + "-closing"; - - function setPanelState(action) - { - $("#docbar").removeClass(openingClass).removeClass(openClass). - removeClass(closingClass); - if (action != "closed") - { - $("#docbar").addClass("docbar" + panelName + "-" + action); - } - } - - function openCloseAnimate(state) - { - function pow(x) - { - x = 1 - x; - x *= x * x; - return 1 - x; - } - - if (state == -1) - { - // startng to open - setPanelState("opening"); - wrapper.css('height', '0'); - } - else if (state < 0) - { - // opening - var height = Math.round(pow(state + 1) * (panelHeight - 1)) + 'px'; - wrapper.css('height', height); - } - else if (state == 0) - { - // open - setPanelState("open"); - wrapper.css('height', panelHeight - 1); - } - else if (state < 1) - { - // closing - setPanelState("closing"); - var height = Math.round((1 - pow(state)) * (panelHeight - 1)) + 'px'; - wrapper.css('height', height); - } - else if (state == 1) - { - // closed - setPanelState("closed"); - wrapper.css('height', '0'); - } - } - - return padutils.makeShowHideAnimator(openCloseAnimate, false, 25, 500); - } - - - var currentPanel = null; - - function setCurrentPanel(newCurrentPanel) - { - if (currentPanel != newCurrentPanel) - { - currentPanel = newCurrentPanel; - padutils.cancelActions("hide-docbar-panel"); - } - } - var panels; - - function changePassword(newPass) - { - if ((newPass || null) != (self.password || null)) - { - self.password = (newPass || null); - pad.notifyChangePassword(newPass); - } - self.renderPassword(); - } - - var pad = undefined; - var self = { - title: null, - password: null, - init: function(opts, _pad) - { - pad = _pad; - - panels = { - impexp: { - animator: getPanelOpenCloseAnimator("impexp", 160) - }, - savedrevs: { - animator: getPanelOpenCloseAnimator("savedrevs", 79) - }, - options: { - animator: getPanelOpenCloseAnimator("options", 114) - }, - security: { - animator: getPanelOpenCloseAnimator("security", 130) - } - }; - - isTitleEditable = opts.isTitleEditable; - self.title = opts.initialTitle; - self.password = opts.initialPassword; - - $("#docbarimpexp").click(function() - { - self.togglePanel("impexp"); - }); - $("#docbarsavedrevs").click(function() - { - self.togglePanel("savedrevs"); - }); - $("#docbaroptions").click(function() - { - self.togglePanel("options"); - }); - $("#docbarsecurity").click(function() - { - self.togglePanel("security"); - }); - - $("#docbarrenamelink").click(self.editTitle); - $("#padtitlesave").click(function() - { - self.closeTitleEdit(true); - }); - $("#padtitlecancel").click(function() - { - self.closeTitleEdit(false); - }); - padutils.bindEnterAndEscape($("#padtitleedit"), function() - { - $("#padtitlesave").trigger('click'); - }, function() - { - $("#padtitlecancel").trigger('click'); - }); - - $("#options-close").click(function() - { - self.setShownPanel(null); - }); - $("#security-close").click(function() - { - self.setShownPanel(null); - }); - - if (pad.getIsProPad()) - { - self.initPassword(); - } - - enabled = true; - self.render(); - - // public/private - $("#security-access input").bind("change click", function(evt) - { - pad.changePadOption('guestPolicy', $("#security-access input[name='padaccess']:checked").val()); - }); - self.setGuestPolicy(opts.guestPolicy); - }, - setGuestPolicy: function(newPolicy) - { - $("#security-access input[value='" + newPolicy + "']").attr("checked", "checked"); - self.render(); - }, - initPassword: function() - { - self.renderPassword(); - $("#password-clearlink").click(function() - { - changePassword(null); - }); - $("#password-setlink, #password-display").click(function() - { - self.enterPassword(); - }); - $("#password-cancellink").click(function() - { - self.exitPassword(false); - }); - $("#password-savelink").click(function() - { - self.exitPassword(true); - }); - padutils.bindEnterAndEscape($("#security-passwordedit"), function() - { - self.exitPassword(true); - }, function() - { - self.exitPassword(false); - }); - }, - enterPassword: function() - { - isEditingPassword = true; - $("#security-passwordedit").val(self.password || ''); - self.renderPassword(); - $("#security-passwordedit").focus().select(); - }, - exitPassword: function(accept) - { - isEditingPassword = false; - if (accept) - { - changePassword($("#security-passwordedit").val()); - } - else - { - self.renderPassword(); - } - }, - renderPassword: function() - { - if (isEditingPassword) - { - $("#password-nonedit").hide(); - $("#password-inedit").show(); - } - else - { - $("#password-nonedit").toggleClass('nopassword', !self.password); - $("#password-setlink").html(self.password ? "Change..." : "Set..."); - if (self.password) - { - $("#password-display").html(self.password.replace(/./g, '•')); - } - else - { - $("#password-display").html("None"); - } - $("#password-inedit").hide(); - $("#password-nonedit").show(); - } - }, - togglePanel: function(panelName) - { - if (panelName in panels) - { - if (currentPanel == panelName) - { - self.setShownPanel(null); - } - else - { - self.setShownPanel(panelName); - } - } - }, - setShownPanel: function(panelName) - { - function animateHidePanel(panelName, next) - { - var delay = 0; - if (panelName == 'options' && isEditingPassword) - { - // give user feedback that the password they've - // typed in won't actually take effect - self.exitPassword(false); - delay = 500; - } - - window.setTimeout(function() - { - panels[panelName].animator.hide(); - if (next) - { - next(); - } - }, delay); - } - - if (!panelName) - { - if (currentPanel) - { - animateHidePanel(currentPanel); - setCurrentPanel(null); - } - } - else if (panelName in panels) - { - if (currentPanel != panelName) - { - if (currentPanel) - { - animateHidePanel(currentPanel, function() - { - panels[panelName].animator.show(); - setCurrentPanel(panelName); - }); - } - else - { - panels[panelName].animator.show(); - setCurrentPanel(panelName); - } - } - } - }, - isPanelShown: function(panelName) - { - if (!panelName) - { - return !currentPanel; - } - else - { - return (panelName == currentPanel); - } - }, - changeTitle: function(newTitle) - { - self.title = newTitle; - self.render(); - }, - editTitle: function() - { - if (!enabled) - { - return; - } - $("#padtitleedit").val(self.title); - isEditingTitle = true; - self.render(); - $("#padtitleedit").focus().select(); - }, - closeTitleEdit: function(accept) - { - if (!enabled) - { - return; - } - if (accept) - { - var newTitle = $("#padtitleedit").val(); - if (newTitle) - { - newTitle = newTitle.substring(0, 80); - self.title = newTitle; - - pad.notifyChangeTitle(newTitle); - } - } - - isEditingTitle = false; - self.render(); - }, - changePassword: function(newPass) - { - if (newPass) - { - self.password = newPass; - } - else - { - self.password = null; - } - self.renderPassword(); - }, - render: function() - { - if (isEditingTitle) - { - $("#docbarpadtitle").hide(); - $("#docbarrenamelink").hide(); - $("#padtitleedit").show(); - $("#padtitlebuttons").show(); - if (!enabled) - { - $("#padtitleedit").attr('disabled', 'disabled'); - } - else - { - $("#padtitleedit").removeAttr('disabled'); - } - } - else - { - $("#padtitleedit").hide(); - $("#padtitlebuttons").hide(); - - var titleSpan = $("#docbarpadtitle span"); - titleSpan.html(padutils.escapeHtml(self.title)); - $("#docbarpadtitle").attr('title', (pad.isPadPublic() ? "Public Pad: " : "") + self.title); - $("#docbarpadtitle").show(); - - if (isTitleEditable) - { - var titleRight = $("#docbarpadtitle").position().left + $("#docbarpadtitle span").position().left + Math.min($("#docbarpadtitle").width(), $("#docbarpadtitle span").width()); - $("#docbarrenamelink").css('left', titleRight + 10).show(); - } - - if (pad.isPadPublic()) - { - $("#docbar").addClass("docbar-public"); - } - else - { - $("#docbar").removeClass("docbar-public"); - } - } - }, - disable: function() - { - enabled = false; - self.render(); - }, - handleResizePage: function() - { - // Side-step circular reference. This should be injected. - var padsavedrevs = require('./pad_savedrevs'); - padsavedrevs.handleResizePage(); - }, - hideLaterIfNoOtherInteraction: function() - { - return padutils.getCancellableAction('hide-docbar-panel', function() - { - self.setShownPanel(null); - }); - } - }; - return self; -}()); - -exports.paddocbar = paddocbar; diff --git a/src/static/js/pad_impexp.js b/src/static/js/pad_impexp.js index 745642569..ae951ca70 100644 --- a/src/static/js/pad_impexp.js +++ b/src/static/js/pad_impexp.js @@ -20,14 +20,11 @@ * limitations under the License. */ -var paddocbar = require('./pad_docbar').paddocbar; - var padimpexp = (function() { ///// import var currentImportTimer = null; - var hidePanelCall = null; function addImportFrames() { @@ -72,7 +69,6 @@ var padimpexp = (function() var ret = window.confirm(html10n.get("pad.impexp.confirmimport")); if (ret) { - hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction(); currentImportTimer = window.setTimeout(function() { if (!currentImportTimer) @@ -255,11 +251,6 @@ var padimpexp = (function() $("#exportopena").attr("href", pad_root_path + "/export/odt"); } - $("#impexp-close").click(function() - { - paddocbar.setShownPanel(null); - }); - addImportFrames(); $("#importfileinput").change(fileInputUpdated); $('#importform').submit(fileInputSubmit); From 0d5b98a338dc6bf2087801820e49ed4d9a35dfc6 Mon Sep 17 00:00:00 2001 From: mluto Date: Thu, 7 Feb 2013 14:30:09 +0100 Subject: [PATCH 024/158] Removed isProPad, since all pads are public except for group-pads --- src/node/handler/PadMessageHandler.js | 1 - src/static/js/pad.js | 6 +----- src/static/js/pad_cookie.js | 2 +- src/static/js/pad_userlist.js | 5 ----- 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 69bb568db..80c79ada8 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -958,7 +958,6 @@ function handleClientReady(client, message) // used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES) "chatHead": pad.chatHead, "numConnectedUsers": roomClients.length, - "isProPad": false, "readOnlyId": padIds.readOnlyPadId, "readonly": padIds.readonly, "serverTimestamp": new Date().getTime(), diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 8b8d16be7..81ae1f85d 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -390,10 +390,6 @@ var pad = { { return clientVars.clientIp; }, - getIsProPad: function() - { - return clientVars.isProPad; - }, getColorPalette: function() { return clientVars.colorPalette; @@ -599,7 +595,7 @@ var pad = { }, isPadPublic: function() { - return (!pad.getIsProPad()) || (pad.getPadOptions().guestPolicy == 'allow'); + return pad.getPadOptions().guestPolicy == 'allow'; }, suggestUserName: function(userId, name) { diff --git a/src/static/js/pad_cookie.js b/src/static/js/pad_cookie.js index 1bb5700ad..9866dbfdd 100644 --- a/src/static/js/pad_cookie.js +++ b/src/static/js/pad_cookie.js @@ -73,7 +73,7 @@ var padcookie = (function() } setRawCookie(stringifyCookie(cookieData)); - if (pad.getIsProPad() && (!getRawCookie()) && (!alreadyWarnedAboutNoCookies)) + if ((!getRawCookie()) && (!alreadyWarnedAboutNoCookies)) { alert("Warning: it appears that your browser does not have cookies enabled." + " EtherPad uses cookies to keep track of unique users for the purpose" + " of putting a quota on the number of active users. Using EtherPad without " + " cookies may fill up your server's user quota faster than expected."); alreadyWarnedAboutNoCookies = true; diff --git a/src/static/js/pad_userlist.js b/src/static/js/pad_userlist.js index d051182b9..962595d2b 100644 --- a/src/static/js/pad_userlist.js +++ b/src/static/js/pad_userlist.js @@ -107,14 +107,9 @@ var paduserlist = (function() function getUserRowHtml(height, data) { var nameHtml; - var isGuest = (data.id.charAt(0) != 'p'); if (data.name) { nameHtml = padutils.escapeHtml(data.name); - if (isGuest && pad.getIsProPad()) - { - nameHtml += ' ('+_(pad.userlist.guest)+')'; - } } else { From fc60ab24fa8fa32283f5d99fd275b85cca68a4d2 Mon Sep 17 00:00:00 2001 From: mluto Date: Thu, 7 Feb 2013 15:34:10 +0100 Subject: [PATCH 025/158] Removed debug code, moved hash-setting after the param-checking --- src/static/js/broadcast_slider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 5acaedaff..e1f7b6c0e 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -107,16 +107,16 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) { newpos = Number(newpos); if (newpos < 0 || newpos > sliderLength) return; - window.location.hash = "#" + newpos; if(!newpos){ newpos = 0; // stops it from displaying NaN if newpos isn't set } + window.location.hash = "#" + newpos; $("#ui-slider-handle").css('left', newpos * ($("#ui-slider-bar").width() - 2) / (sliderLength * 1.0)); $("a.tlink").map(function() { $(this).attr('href', $(this).attr('thref').replace("%revision%", newpos)); }); - console.log(newpos); + $("#revision_label").html(html10n.get("timeslider.version", { "version": newpos})); if (newpos == 0) From f42d3820a5f495fbee4c5c1579b9c2daaf2e0e49 Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 7 Feb 2013 15:24:30 +0000 Subject: [PATCH 026/158] fix timeslider tests --- tests/frontend/specs/timeslider_revisions.js | 26 +++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index 52f487643..bb0696c7c 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -4,7 +4,6 @@ describe("timeslider", function(){ helper.newPad(cb); this.timeout(6000); }); - xit("loads adds a hundred revisions", function(done) { var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; @@ -57,14 +56,13 @@ describe("timeslider", function(){ }, 6000); }, revs*timePerRev); }); - it("changes the url when clicking on the timeslider", function(done) { var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; // make some changes to produce 7 revisions - var timePerRev = 900 - , revs = 7; + var timePerRev = 1000 + , revs = 20; this.timeout(revs*timePerRev+10000); for(var i=0; i < revs; i++) { setTimeout(function() { @@ -100,28 +98,32 @@ describe("timeslider", function(){ }, 6000); }, revs*timePerRev); }); - + + // This test is bad because it expects char length to be static + // A much better way would be get the charCount before sending new chars it("jumps to a revision given in the url", function(done) { var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; - this.timeout(11000); + this.timeout(15000); inner$("div").first().sendkeys('a'); setTimeout(function() { - // go to timeslider + // go to timeslider with a specific revision set $('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0'); var timeslider$; helper.waitFor(function(){ - timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; - return timeslider$ && timeslider$('#padcontent').text().length == 230; + try{ + timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; + }catch(e){ + } + return timeslider$('#padcontent').text().length == 230; }, 6000).always(function(){ expect( timeslider$('#padcontent').text().length ).to.eql( 230 ); done(); }); }, 2500); }); - it("checks the export url", function(done) { var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; @@ -135,7 +137,9 @@ describe("timeslider", function(){ var exportLink; helper.waitFor(function(){ - timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; + try{ + timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; + }catch(e){} if(!timeslider$) return false; exportLink = timeslider$('#exportplaina').attr('href'); From 8c0fb090ebdcb8c7adb3fb084e9a7dba9d4b953d Mon Sep 17 00:00:00 2001 From: John McLear Date: Thu, 7 Feb 2013 15:50:59 +0000 Subject: [PATCH 027/158] works in all 3 major browsers --- tests/frontend/specs/timeslider_revisions.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index bb0696c7c..6eb4cca61 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -98,7 +98,6 @@ describe("timeslider", function(){ }, 6000); }, revs*timePerRev); }); - // This test is bad because it expects char length to be static // A much better way would be get the charCount before sending new chars it("jumps to a revision given in the url", function(done) { @@ -117,7 +116,9 @@ describe("timeslider", function(){ timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; }catch(e){ } - return timeslider$('#padcontent').text().length == 230; + if(timeslider$){ + return timeslider$('#padcontent').text().length == 230; + } }, 6000).always(function(){ expect( timeslider$('#padcontent').text().length ).to.eql( 230 ); done(); From ae6a9c25ae828ba0a42b34b475d988ec8a7fb3df Mon Sep 17 00:00:00 2001 From: mluto Date: Thu, 7 Feb 2013 17:34:31 +0100 Subject: [PATCH 028/158] Fixed "jumps to a revision given in the url" test --- tests/frontend/specs/timeslider_revisions.js | 57 ++++++++++++-------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index 6eb4cca61..54af44ba3 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -98,32 +98,47 @@ describe("timeslider", function(){ }, 6000); }, revs*timePerRev); }); - // This test is bad because it expects char length to be static - // A much better way would be get the charCount before sending new chars it("jumps to a revision given in the url", function(done) { var inner$ = helper.padInner$; - var chrome$ = helper.padChrome$; - this.timeout(15000); - inner$("div").first().sendkeys('a'); - - setTimeout(function() { - // go to timeslider with a specific revision set - $('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0'); - var timeslider$; + var chrome$ = helper.padChrome$; + this.timeout(20000); + + // wait for the text to be loaded + helper.waitFor(function(){ + return inner$('body').text().length != 0; + }, 6000).always(function() { + var newLines = inner$('body div').length; + var oldLength = inner$('body').text().length + newLines / 2; + expect( oldLength ).to.not.eql( 0 ); + inner$("div").first().sendkeys('a'); + // wait for our additional revision to be added helper.waitFor(function(){ - try{ - timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; - }catch(e){ - } - if(timeslider$){ - return timeslider$('#padcontent').text().length == 230; - } - }, 6000).always(function(){ - expect( timeslider$('#padcontent').text().length ).to.eql( 230 ); - done(); + // newLines takes the new lines into account which are strippen when using + // inner$('body').text(), one
is used for one line in ACE. + var lenOkay = inner$('body').text().length + newLines / 2 != oldLength; + // this waits for the color to be added to our , which means that the revision + // was accepted by the server. + var colorOkay = inner$('span').first().attr('class').indexOf("author-") == 0; + return lenOkay && colorOkay; + }, 6000).always(function() { + // go to timeslider with a specific revision set + $('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0'); + + // wait for the timeslider to be loaded + helper.waitFor(function(){ + try { + timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; + } catch(e){} + if(timeslider$){ + return timeslider$('#padcontent').text().length == oldLength; + } + }, 6000).always(function(){ + expect( timeslider$('#padcontent').text().length ).to.eql( oldLength ); + done(); + }); }); - }, 2500); + }); }); it("checks the export url", function(done) { var inner$ = helper.padInner$; From e6454af5ed6b67976c32adac74ecdd4279f2fe07 Mon Sep 17 00:00:00 2001 From: mluto Date: Fri, 8 Feb 2013 22:00:17 +0100 Subject: [PATCH 029/158] Removed fullWidth/resizing-code as the pad is always fullscreen, unlike the old EP --- src/node/handler/PadMessageHandler.js | 1 - src/static/js/broadcast_slider.js | 17 +---------------- src/static/js/pad.js | 23 ----------------------- 3 files changed, 1 insertion(+), 40 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 80c79ada8..1840cab35 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -964,7 +964,6 @@ function handleClientReady(client, message) "globalPadId": message.padId, "userId": author, "cookiePrefsToSet": { - "fullWidth": false, "hideSidebar": false }, "abiwordAvailable": settings.abiwordAvailable(), diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index 5acaedaff..a915eacde 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -460,26 +460,11 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) if (clientVars) { - if (clientVars.fullWidth) - { - $("#padpage").css('width', '100%'); - $("#revision").css('position', "absolute") - $("#revision").css('right', "20px") - $("#revision").css('top', "20px") - $("#padmain").css('left', '0px'); - $("#padmain").css('right', '197px'); - $("#padmain").css('width', 'auto'); - $("#rightbars").css('right', '7px'); - $("#rightbars").css('margin-right', '0px'); - $("#timeslider").css('width', 'auto'); - } - if (clientVars.disableRightBar) { $("#rightbars").css('display', 'none'); $('#padmain').css('width', 'auto'); - if (clientVars.fullWidth) $("#padmain").css('right', '7px'); - else $("#padmain").css('width', '860px'); + $("#padmain").css('width', '860px'); $("#revision").css('position', "absolute"); $("#revision").css('right', "20px"); $("#revision").css('top', "20px"); diff --git a/src/static/js/pad.js b/src/static/js/pad.js index 81ae1f85d..b70029898 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -464,7 +464,6 @@ var pad = { // order of inits is important here: padcookie.init(clientVars.cookiePrefsToSet, this); - $("#widthprefcheck").click(pad.toggleWidthPref); // $("#sidebarcheck").click(pad.togglewSidebar); pad.myUserInfo = { @@ -829,13 +828,6 @@ var pad = { $('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges())); $('form#reconnectform').submit(); }, - toggleWidthPref: function() - { - var newValue = !padcookie.getPref('fullWidth'); - padcookie.setPref('fullWidth', newValue); - $("#widthprefcheck").toggleClass('widthprefchecked', !! newValue).toggleClass('widthprefunchecked', !newValue); - pad.handleWidthChange(); - }, /* toggleSidebar: function() { @@ -845,21 +837,6 @@ var pad = { pad.determineSidebarVisibility(); }, */ - handleWidthChange: function() - { - var isFullWidth = padcookie.getPref('fullWidth'); - if (isFullWidth) - { - $("body").addClass('fullwidth').removeClass('limwidth').removeClass('squish1width').removeClass('squish2width'); - } - else - { - $("body").addClass('limwidth').removeClass('fullwidth'); - - var pageWidth = $(window).width(); - $("body").toggleClass('squish1width', (pageWidth < 912 && pageWidth > 812)).toggleClass('squish2width', (pageWidth <= 812)); - } - }, // this is called from code put into a frame from the server: handleImportExportFrameCall: function(callName, varargs) { From f2cd9e3adc3968b14ff0f3e9321abd65cbc52ecf Mon Sep 17 00:00:00 2001 From: mluto Date: Fri, 8 Feb 2013 22:06:28 +0100 Subject: [PATCH 030/158] Removed sidebar-code since the sidebar got removed in EPL, there is only the stickychat which is not affected by this. --- src/node/handler/PadMessageHandler.js | 3 --- src/static/js/broadcast_slider.js | 10 --------- src/static/js/pad.js | 31 --------------------------- 3 files changed, 44 deletions(-) diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 1840cab35..19ebfe92e 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -963,9 +963,6 @@ function handleClientReady(client, message) "serverTimestamp": new Date().getTime(), "globalPadId": message.padId, "userId": author, - "cookiePrefsToSet": { - "hideSidebar": false - }, "abiwordAvailable": settings.abiwordAvailable(), "plugins": { "plugins": plugins.plugins, diff --git a/src/static/js/broadcast_slider.js b/src/static/js/broadcast_slider.js index a915eacde..b8dbd9ffd 100644 --- a/src/static/js/broadcast_slider.js +++ b/src/static/js/broadcast_slider.js @@ -460,16 +460,6 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded) if (clientVars) { - if (clientVars.disableRightBar) - { - $("#rightbars").css('display', 'none'); - $('#padmain').css('width', 'auto'); - $("#padmain").css('width', '860px'); - $("#revision").css('position', "absolute"); - $("#revision").css('right', "20px"); - $("#revision").css('top', "20px"); - } - $("#timeslider").show(); var startPos = clientVars.collab_client_vars.rev; diff --git a/src/static/js/pad.js b/src/static/js/pad.js index b70029898..6e8b2ae01 100644 --- a/src/static/js/pad.js +++ b/src/static/js/pad.js @@ -462,10 +462,6 @@ var pad = { } // order of inits is important here: - padcookie.init(clientVars.cookiePrefsToSet, this); - - // $("#sidebarcheck").click(pad.togglewSidebar); - pad.myUserInfo = { userId: clientVars.userId, name: clientVars.userName, @@ -745,28 +741,10 @@ var pad = { }, 1000); } - // pad.determineSidebarVisibility(isConnected && !isInitialConnect); pad.determineChatVisibility(isConnected && !isInitialConnect); pad.determineAuthorshipColorsVisibility(); }, -/* determineSidebarVisibility: function(asNowConnectedFeedback) - { - if (pad.isFullyConnected()) - { - var setSidebarVisibility = padutils.getCancellableAction("set-sidebar-visibility", function() - { - // $("body").toggleClass('hidesidebar', !! padcookie.getPref('hideSidebar')); - }); - window.setTimeout(setSidebarVisibility, asNowConnectedFeedback ? 3000 : 0); - } - else - { - padutils.cancelActions("set-sidebar-visibility"); - $("body").removeClass('hidesidebar'); - } - }, -*/ determineChatVisibility: function(asNowConnectedFeedback){ var chatVisCookie = padcookie.getPref('chatAlwaysVisible'); if(chatVisCookie){ // if the cookie is set for chat always visible @@ -828,15 +806,6 @@ var pad = { $('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges())); $('form#reconnectform').submit(); }, -/* - toggleSidebar: function() - { - var newValue = !padcookie.getPref('hideSidebar'); - padcookie.setPref('hideSidebar', newValue); - $("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass('sidebarunchecked', !! newValue); - pad.determineSidebarVisibility(); - }, -*/ // this is called from code put into a frame from the server: handleImportExportFrameCall: function(callName, varargs) { From bb1d4626126ace07c02b8bc935df45b401439539 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 9 Feb 2013 15:45:00 +0000 Subject: [PATCH 031/158] plugin test docs --- doc/plugins.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/plugins.md b/doc/plugins.md index afa01316b..4b09eb424 100644 --- a/doc/plugins.md +++ b/doc/plugins.md @@ -104,4 +104,16 @@ Your plugin must also contain a [package definition file](http://npmjs.org/doc/j ``` ## Templates -If your plugin adds or modifies the front end HTML (e.g. adding buttons or changing their functions), you should put the necessary HTML code for such operations in `templates/`, in files of type ".ejs", since Etherpad-Lite uses EJS for HTML templating. See the following link for more information about EJS: . \ No newline at end of file +If your plugin adds or modifies the front end HTML (e.g. adding buttons or changing their functions), you should put the necessary HTML code for such operations in `templates/`, in files of type ".ejs", since Etherpad-Lite uses EJS for HTML templating. See the following link for more information about EJS: . + +## Writing and running front-end tests for your plugin + +Etherpad allows you to easily create front-end tests for plugins. + +1. Create a new folder +``` +%your_plugin%/static/tests/frontend +``` +2. Put your spec file in here (Example spec files are visible in %etherpad_root_folder%/frontend/tests) + +3. Visit http://yourserver.com/frontend/tests your front-end tests will run. From ef0a6ce5ff5e1f32f819b6ebab07de01427e0179 Mon Sep 17 00:00:00 2001 From: 0ip Date: Sat, 9 Feb 2013 17:39:08 +0100 Subject: [PATCH 032/158] Remove top-border --- src/static/css/admin.css | 7 ------- src/templates/admin/index.html | 1 - src/templates/admin/plugins-info.html | 1 - src/templates/admin/plugins.html | 1 - src/templates/admin/settings.html | 2 -- 5 files changed, 12 deletions(-) diff --git a/src/static/css/admin.css b/src/static/css/admin.css index 9ea02744f..db4def2d6 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -9,13 +9,6 @@ body { background: -o-radial-gradient(circle,#aaa,#eee 60%) center fixed; } -#topborder { - border-top: 8px solid rgba(51, 51, 51, 0.8); - position: fixed; - top: 0px; - width: 100%; -} - div.menu { background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.75); box-shadow: 0px -4px 4px rgba(0, 0, 0, 0.3); diff --git a/src/templates/admin/index.html b/src/templates/admin/index.html index 0e5c16edd..f8ec0e898 100644 --- a/src/templates/admin/index.html +++ b/src/templates/admin/index.html @@ -19,6 +19,5 @@
-
diff --git a/src/templates/admin/plugins-info.html b/src/templates/admin/plugins-info.html index 648e7d6d0..8152209c9 100644 --- a/src/templates/admin/plugins-info.html +++ b/src/templates/admin/plugins-info.html @@ -38,6 +38,5 @@
-
diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index bfe535e7e..2306eff68 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -92,6 +92,5 @@ -
diff --git a/src/templates/admin/settings.html b/src/templates/admin/settings.html index 0fb1c9ae1..650c0bef9 100644 --- a/src/templates/admin/settings.html +++ b/src/templates/admin/settings.html @@ -42,7 +42,5 @@
- -
From a93ff378796cfdff62040e332d1d831e3cca373e Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 9 Feb 2013 16:42:47 +0000 Subject: [PATCH 033/158] fix opera by recreating a scheduler object --- src/static/js/ace2_inner.js | 39 +++++++++++++++++-------------- src/static/js/changesettracker.js | 2 +- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/static/js/ace2_inner.js b/src/static/js/ace2_inner.js index 6d8c58bbc..50567965b 100644 --- a/src/static/js/ace2_inner.js +++ b/src/static/js/ace2_inner.js @@ -154,16 +154,21 @@ function Ace2Inner(){ var dmesg = noop; window.dmesg = noop; + + var scheduler = parent; // hack for opera required + // Ugly hack for Firefox 18 // get the timeout and interval methods from the parent iframe - // This hack breaks IE8 - try{ - setTimeout = parent.setTimeout; - clearTimeout = parent.clearTimeout; - setInterval = parent.setInterval; - clearInterval = parent.clearInterval; - }catch(err){ - // IE8 can panic here. + // This hack breaks IE8 so be careful + if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)){ + try{ + setTimeout = scheduler.setTimeout; + clearTimeout = scheduler.clearTimeout; + setInterval = scheduler.setInterval; + clearInterval = scheduler.clearInterval; + }catch(err){ + // IE8 can panic here. + } } var textFace = 'monospace'; @@ -184,7 +189,7 @@ function Ace2Inner(){ parentDynamicCSS = makeCSSManager("dynamicsyntax", true); } - var changesetTracker = makeChangesetTracker(rep.apool, { + var changesetTracker = makeChangesetTracker(scheduler, rep.apool, { withCallbacks: function(operationName, f) { inCallStackIfNecessary(operationName, function() @@ -604,7 +609,7 @@ function Ace2Inner(){ doesWrap = newVal; var dwClass = "doesWrap"; setClassPresence(root, "doesWrap", doesWrap); - setTimeout(function() + scheduler.setTimeout(function() { inCallStackIfNecessary("setWraps", function() { @@ -644,7 +649,7 @@ function Ace2Inner(){ textFace = face; root.style.fontFamily = textFace; lineMetricsDiv.style.fontFamily = textFace; - setTimeout(function() + scheduler.setTimeout(function() { setUpTrackingCSS(); }, 0); @@ -657,7 +662,7 @@ function Ace2Inner(){ root.style.lineHeight = textLineHeight() + "px"; sideDiv.style.lineHeight = textLineHeight() + "px"; lineMetricsDiv.style.fontSize = textSize + "px"; - setTimeout(function() + scheduler.setTimeout(function() { setUpTrackingCSS(); }, 0); @@ -1106,7 +1111,7 @@ function Ace2Inner(){ scheduledTime = time; var delay = time - now(); if (delay < 0) delay = 0; - scheduledTimeout = setTimeout(callback, delay); + scheduledTimeout = scheduler.setTimeout(callback, delay); } function callback() @@ -3623,7 +3628,7 @@ function Ace2Inner(){ evt.preventDefault(); doReturnKey(); //scrollSelectionIntoView(); - setTimeout(function() + scheduler.setTimeout(function() { outerWin.scrollBy(-100, 0); }, 0); @@ -3709,7 +3714,7 @@ function Ace2Inner(){ var isPageDown = evt.which === 34; var isPageUp = evt.which === 33; - setTimeout(function(){ + scheduler.setTimeout(function(){ var newVisibleLineRange = getVisibleLineRange(); var linesCount = rep.lines.length(); @@ -4766,7 +4771,7 @@ function Ace2Inner(){ }); - setTimeout(function() + scheduler.setTimeout(function() { parent.readyFunc(); // defined in code that sets up the inner iframe }, 0); @@ -5214,7 +5219,7 @@ function Ace2Inner(){ documentAttributeManager: documentAttributeManager }); - setTimeout(function() + scheduler.setTimeout(function() { parent.readyFunc(); // defined in code that sets up the inner iframe }, 0); diff --git a/src/static/js/changesettracker.js b/src/static/js/changesettracker.js index 91e854d65..16a23cdbc 100644 --- a/src/static/js/changesettracker.js +++ b/src/static/js/changesettracker.js @@ -23,7 +23,7 @@ var AttributePool = require('./AttributePool'); var Changeset = require('./Changeset'); -function makeChangesetTracker(apool, aceCallbacksProvider) +function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) { // latest official text from server From dd29096ad71e70c3b2c15d807a4ff6872d572320 Mon Sep 17 00:00:00 2001 From: 0ip Date: Sat, 9 Feb 2013 17:47:52 +0100 Subject: [PATCH 034/158] Normalize view-ports --- src/templates/admin/index.html | 2 +- src/templates/admin/plugins-info.html | 2 +- src/templates/admin/plugins.html | 2 +- src/templates/admin/settings.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/templates/admin/index.html b/src/templates/admin/index.html index f8ec0e898..8412ca1cd 100644 --- a/src/templates/admin/index.html +++ b/src/templates/admin/index.html @@ -1,7 +1,7 @@ Admin Dashboard - Etherpad lite - + diff --git a/src/templates/admin/plugins-info.html b/src/templates/admin/plugins-info.html index 8152209c9..b833063d9 100644 --- a/src/templates/admin/plugins-info.html +++ b/src/templates/admin/plugins-info.html @@ -5,7 +5,7 @@ Plugin information - Etherpad lite - + diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 2306eff68..27cd9ca5a 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -1,7 +1,7 @@ Plugin manager - Etherpad lite - + diff --git a/src/templates/admin/settings.html b/src/templates/admin/settings.html index 650c0bef9..9c195df54 100644 --- a/src/templates/admin/settings.html +++ b/src/templates/admin/settings.html @@ -1,7 +1,7 @@ Settings - Etherpad lite - + From b04e99bddc9384d3683075a39309fab817e16065 Mon Sep 17 00:00:00 2001 From: 0ip Date: Sat, 9 Feb 2013 17:54:39 +0100 Subject: [PATCH 035/158] Tweak admin-UI --- src/static/css/admin.css | 141 ++++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 33 deletions(-) diff --git a/src/static/css/admin.css b/src/static/css/admin.css index db4def2d6..c35aca310 100644 --- a/src/static/css/admin.css +++ b/src/static/css/admin.css @@ -1,59 +1,59 @@ +html, body { + height: 100%; + box-sizing: border-box; +} + body { margin: 0; color: #333; font: 14px helvetica, sans-serif; - background: #ddd; - background: -webkit-radial-gradient(circle,#aaa,#eee 60%) center fixed; - background: -moz-radial-gradient(circle,#aaa,#eee 60%) center fixed; - background: -ms-radial-gradient(circle,#aaa,#eee 60%) center fixed; - background: -o-radial-gradient(circle,#aaa,#eee 60%) center fixed; + background: #eee; } div.menu { - background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.75); - box-shadow: 0px -4px 4px rgba(0, 0, 0, 0.3); - display: block; - float: left; - height: 100%; - padding: 15px; - position: fixed; - width: 220px; + height: 100%; + padding: 15px; + width: 220px; + border-right: 1px solid #ccc; + position: fixed; +} + +div.menu ul { + padding: 0; } div.menu li { list-style: none; margin-left: 3px; - line-height: 1.6 + line-height: 3; + border-top: 1px solid #ccc; +} + +div.menu li:last-child { + border-bottom: 1px solid #ccc; } div.innerwrapper { - display: block; - float: right; - opacity: 0.9; padding: 15px; - max-width: 860px; - border-radius: 0 0 7px 7px; - margin-left:250px; - min-width:400px; - width:100%; + padding-left: 265px; } #wrapper { background: none repeat scroll 0px 0px #FFFFFF; - box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.3); + box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.2); margin: auto; max-width: 1150px; min-height: 100%; - overflow: auto; - padding-left: 15px; - opacity: .9; } + h1 { font-size: 29px; } + h2 { font-size: 24px; } + .separator { margin: 10px 0; height: 1px; @@ -63,37 +63,45 @@ h2 { background: -ms-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff); background: -o-linear-gradient(left, #fff, #aaa 20%, #aaa 80%, #fff); } + form { margin-bottom: 0; } + #inner { width: 300px; margin: 0 auto; } + input { font-weight: bold; font-size: 15px; } + input[type="button"] { padding: 4px 6px; margin: 0; } + table input[type="button"] { float: right; width: 100px; } + input[type="text"] { border-radius: 3px; box-sizing: border-box; -moz-box-sizing: border-box; padding: 10px; - *padding: 0; /* IE7 hack */ + *padding: 0; + /* IE7 hack */ width: 100%; outline: none; border: 1px solid #ddd; margin: 0 0 5px 0; max-width: 500px; } + table { border: 1px solid #ddd; border-radius: 3px; @@ -101,34 +109,101 @@ table { width: 100%; margin: 20px 0; } + table thead tr { background: #eee; } + td, th { padding: 5px; } + .template { display: none; } + #progress { position: absolute; bottom: 50px; } + .settings { - margin-top:10px; - width:100%; - min-height:600px; + outline: none; + width: 100%; + min-height: 500px; } -#response{ - display:inline; + +#response { + display: inline; } a:link, a:visited, a:hover, a:focus { color: #333333; text-decoration: none; - border-bottom: #333333 1px dotted; } a:focus, a:hover { border-bottom: #333333 1px solid; } + +@media (max-width: 720px) { + div.innerwrapper { + padding: 0 15px 15px 15px; + } + + div.menu { + padding: 1px 15px 0 15px; + position: static; + height: auto; + border-right: none; + width: auto; + } + + table { + border: none; + } + + table, thead, tbody, td, tr { + display: block; + } + + thead tr { + display: none; + } + + tr { + border: 1px solid #ccc; + margin-bottom: 5px; + border-radius: 3px; + } + + td { + border: none; + border-bottom: 1px solid #eee; + position: relative; + padding-left: 50%; + white-space: normal; + text-align: left; + } + + td.name { + word-wrap: break-word; + } + + td:before { + padding-right: 10px; + white-space: nowrap; + float: left; + margin-left: -100%; + font-weight: bold; + content: attr(data-label); + } + + td:last-child { + border-bottom: none; + } + + table input[type="button"] { + float: none; + } +} \ No newline at end of file From 34fd7726bda0df6cc84774558e26b58d60a25fcd Mon Sep 17 00:00:00 2001 From: 0ip Date: Sat, 9 Feb 2013 17:55:11 +0100 Subject: [PATCH 036/158] Fix :admin/index links --- src/templates/admin/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/templates/admin/index.html b/src/templates/admin/index.html index 8412ca1cd..77d18263e 100644 --- a/src/templates/admin/index.html +++ b/src/templates/admin/index.html @@ -12,9 +12,9 @@

Etherpad lite

From 280b3a404cadf2dbc3043d3f47b6e337f5c69731 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 9 Feb 2013 17:02:54 +0000 Subject: [PATCH 037/158] doh, doc fix --- doc/plugins.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/plugins.md b/doc/plugins.md index 4b09eb424..0e0dee008 100644 --- a/doc/plugins.md +++ b/doc/plugins.md @@ -112,8 +112,8 @@ Etherpad allows you to easily create front-end tests for plugins. 1. Create a new folder ``` -%your_plugin%/static/tests/frontend +%your_plugin%/static/tests/frontend/specs ``` -2. Put your spec file in here (Example spec files are visible in %etherpad_root_folder%/frontend/tests) +2. Put your spec file in here (Example spec files are visible in %etherpad_root_folder%/frontend/tests/specs) 3. Visit http://yourserver.com/frontend/tests your front-end tests will run. From 6651ed0b7a41485abcb0caa5019d3221407518dc Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 9 Feb 2013 17:18:13 +0000 Subject: [PATCH 038/158] allow all timeslider tests to run --- tests/frontend/specs/timeslider_revisions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/frontend/specs/timeslider_revisions.js b/tests/frontend/specs/timeslider_revisions.js index 54af44ba3..679381349 100644 --- a/tests/frontend/specs/timeslider_revisions.js +++ b/tests/frontend/specs/timeslider_revisions.js @@ -4,7 +4,7 @@ describe("timeslider", function(){ helper.newPad(cb); this.timeout(6000); }); - xit("loads adds a hundred revisions", function(done) { + it("loads adds a hundred revisions", function(done) { var inner$ = helper.padInner$; var chrome$ = helper.padChrome$; From d2409288be75b3027de3210be92dc8e5f0c52ef1 Mon Sep 17 00:00:00 2001 From: 0ip Date: Sat, 9 Feb 2013 20:15:41 +0100 Subject: [PATCH 039/158] Add data-title for responsive tables --- src/templates/admin/plugins.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 27cd9ca5a..92ec0a16a 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -43,9 +43,9 @@ - - - + + + @@ -74,9 +74,9 @@ - - - + + + From 52ff28dea03a342c13d408d1c588ccdc38b56b68 Mon Sep 17 00:00:00 2001 From: 0ip Date: Sat, 9 Feb 2013 20:34:12 +0100 Subject: [PATCH 040/158] Fix data attribute --- src/templates/admin/plugins.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/templates/admin/plugins.html b/src/templates/admin/plugins.html index 92ec0a16a..51cebafb7 100644 --- a/src/templates/admin/plugins.html +++ b/src/templates/admin/plugins.html @@ -43,9 +43,9 @@ - - - + + + @@ -74,9 +74,9 @@ - - - + + + From 21e1422be6632f2cf407dba916f05a30110dc532 Mon Sep 17 00:00:00 2001 From: 0ip Date: Sat, 9 Feb 2013 20:42:47 +0100 Subject: [PATCH 041/158] Drop 'lite' occurrences in /admin/* --- src/templates/admin/index.html | 4 ++-- src/templates/admin/plugins-info.html | 4 ++-- src/templates/admin/plugins.html | 4 ++-- src/templates/admin/settings.html | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/templates/admin/index.html b/src/templates/admin/index.html index 77d18263e..f1a74afa5 100644 --- a/src/templates/admin/index.html +++ b/src/templates/admin/index.html @@ -1,6 +1,6 @@ - Admin Dashboard - Etherpad lite + Admin Dashboard - Etherpad @@ -9,7 +9,7 @@