editor.html now boots with a bit of the plugin system
This commit is contained in:
parent
bb6854aeeb
commit
01e9fdf772
10 changed files with 2063 additions and 186 deletions
125
demo/boot.js
Normal file
125
demo/boot.js
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (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.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Skywriter.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Kevin Dangoor (kdangoor@mozilla.com)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
// TODO: Yuck! A global function
|
||||||
|
var setupPlugins = function(config, callback) {
|
||||||
|
config = config || {};
|
||||||
|
if (!config.pluginDirs) {
|
||||||
|
config.pluginDirs = {};
|
||||||
|
}
|
||||||
|
// config.pluginDirs["../lib"] = {
|
||||||
|
// packages: ["ace"]
|
||||||
|
// };
|
||||||
|
config.pluginDirs["../plugins"] = {
|
||||||
|
packages: ["pilot"]
|
||||||
|
};
|
||||||
|
|
||||||
|
var knownPlugins = [];
|
||||||
|
|
||||||
|
var pluginPackageInfo = {
|
||||||
|
"../lib": [
|
||||||
|
{
|
||||||
|
name: "ace",
|
||||||
|
lib: "."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var paths = {};
|
||||||
|
var i;
|
||||||
|
var location;
|
||||||
|
|
||||||
|
// we need to ensure that the core plugin directory is loaded first
|
||||||
|
var pluginDirs = [];
|
||||||
|
var pluginDir;
|
||||||
|
for (pluginDir in config.pluginDirs) {
|
||||||
|
pluginDirs.push(pluginDir);
|
||||||
|
}
|
||||||
|
pluginDirs.sort(function(a, b) {
|
||||||
|
if (a == "../plugins") {
|
||||||
|
return -1;
|
||||||
|
} else if (b == "../plugins") {
|
||||||
|
return 1;
|
||||||
|
} else if (a < b) {
|
||||||
|
return -1;
|
||||||
|
} else if (b < a) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// set up RequireJS to know that our plugins all have a main module called "index"
|
||||||
|
for (var dirNum = 0; dirNum < pluginDirs.length; dirNum++) {
|
||||||
|
pluginDir = pluginDirs[dirNum];
|
||||||
|
var dirInfo = config.pluginDirs[pluginDir];
|
||||||
|
if (dirInfo.packages) {
|
||||||
|
location = pluginPackageInfo[pluginDir];
|
||||||
|
if (location === undefined) {
|
||||||
|
pluginPackageInfo[pluginDir] = location = [];
|
||||||
|
}
|
||||||
|
var packages = dirInfo.packages;
|
||||||
|
for (i = 0; i < packages.length; i++) {
|
||||||
|
location.push({
|
||||||
|
name: packages[i],
|
||||||
|
main: "index"
|
||||||
|
});
|
||||||
|
knownPlugins.push(packages[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dirInfo.singleFiles) {
|
||||||
|
for (i = 0; i < dirInfo.singleFiles.length; i++) {
|
||||||
|
var pluginName = dirInfo.singleFiles[i];
|
||||||
|
paths[pluginName] = pluginDir + "/" + pluginName;
|
||||||
|
knownPlugins.push(pluginName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require({
|
||||||
|
packagePaths: pluginPackageInfo,
|
||||||
|
paths: paths
|
||||||
|
});
|
||||||
|
require(["pilot/plugin_manager"], function() {
|
||||||
|
var pluginsModule = require("pilot/plugin_manager");
|
||||||
|
var catalog = pluginsModule.catalog;
|
||||||
|
catalog.registerPlugins(knownPlugins);
|
||||||
|
if (callback) {
|
||||||
|
callback(pluginsModule);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
209
demo/demo_startup.js
Normal file
209
demo/demo_startup.js
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (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.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Skywriter.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Fabian Jakobs <fabian AT ajax DOT org>
|
||||||
|
* Kevin Dangoor (kdangoor@mozilla.com)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
exports.launch = function() {
|
||||||
|
|
||||||
|
var eventMod = require("ace/lib/event");
|
||||||
|
var editorMod = require("ace/editor");
|
||||||
|
var renderMod = require("ace/virtual_renderer");
|
||||||
|
var theme = require("ace/theme/textmate");
|
||||||
|
var docMod = require("ace/document");
|
||||||
|
var jsMod = require("ace/mode/javascript");
|
||||||
|
var cssMod = require("ace/mode/css");
|
||||||
|
var htmlMod = require("ace/mode/html");
|
||||||
|
var xmlMod = require("ace/mode/xml");
|
||||||
|
var textMod = require("ace/mode/text");
|
||||||
|
var undoMod = require("ace/undomanager");
|
||||||
|
|
||||||
|
var event = eventMod.event;
|
||||||
|
var Editor = editorMod.Editor;
|
||||||
|
var Renderer = renderMod.VirtualRenderer;
|
||||||
|
var Document = docMod.Document;
|
||||||
|
var JavaScriptMode = jsMod.JavaScript;
|
||||||
|
var CssMode = cssMod.Css;
|
||||||
|
var HtmlMode = htmlMod.Html;
|
||||||
|
var XmlMode = xmlMod.Xml;
|
||||||
|
var TextMode = textMod.Text;
|
||||||
|
var UndoManager = undoMod.UndoManager;
|
||||||
|
|
||||||
|
var docs = {}
|
||||||
|
|
||||||
|
docs.js = new Document(document.getElementById("jstext").innerHTML);
|
||||||
|
docs.js.setMode(new JavaScriptMode());
|
||||||
|
docs.js.setUndoManager(new UndoManager());
|
||||||
|
|
||||||
|
docs.css = new Document(document.getElementById("csstext").innerHTML);
|
||||||
|
docs.css.setMode(new CssMode());
|
||||||
|
docs.css.setUndoManager(new UndoManager());
|
||||||
|
|
||||||
|
docs.html = new Document(document.getElementById("htmltext").innerHTML);
|
||||||
|
docs.html.setMode(new HtmlMode());
|
||||||
|
docs.html.setUndoManager(new UndoManager());
|
||||||
|
|
||||||
|
var docEl = document.getElementById("doc");
|
||||||
|
|
||||||
|
function onDocChange() {
|
||||||
|
var doc = getDoc();
|
||||||
|
editor.setDocument(doc);
|
||||||
|
|
||||||
|
var mode = doc.getMode();
|
||||||
|
if (mode instanceof JavaScriptMode) {
|
||||||
|
modeEl.value = "javascript"
|
||||||
|
}
|
||||||
|
else if (mode instanceof CssMode) {
|
||||||
|
modeEl.value = "css"
|
||||||
|
}
|
||||||
|
else if (mode instanceof HtmlMode) {
|
||||||
|
modeEl.value = "html"
|
||||||
|
}
|
||||||
|
else if (mode instanceof XmlMode) {
|
||||||
|
modeEl.value = "xml"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
modeEl.value = "text"
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.focus();
|
||||||
|
}
|
||||||
|
docEl.onchange = onDocChange;
|
||||||
|
|
||||||
|
function getDoc() {
|
||||||
|
return docs[docEl.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
var modeEl = document.getElementById("mode");
|
||||||
|
modeEl.onchange = function() {
|
||||||
|
editor.getDocument().setMode(modes[modeEl.value] || modes.text);
|
||||||
|
};
|
||||||
|
|
||||||
|
var modes = {
|
||||||
|
text: new TextMode(),
|
||||||
|
xml: new XmlMode(),
|
||||||
|
html: new HtmlMode(),
|
||||||
|
css: new CssMode(),
|
||||||
|
javascript: new JavaScriptMode()
|
||||||
|
};
|
||||||
|
|
||||||
|
function getMode() {
|
||||||
|
return modes[modeEl.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeEl = document.getElementById("theme");
|
||||||
|
themeEl.onchange = function() {
|
||||||
|
editor.setTheme(themeEl.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectEl = document.getElementById("select_style");
|
||||||
|
selectEl.onchange = function() {
|
||||||
|
if (selectEl.checked) {
|
||||||
|
editor.setSelectionStyle("line");
|
||||||
|
} else {
|
||||||
|
editor.setSelectionStyle("text");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var activeEl = document.getElementById("highlight_active");
|
||||||
|
activeEl.onchange = function() {
|
||||||
|
editor.setHighlightActiveLine(!!activeEl.checked);
|
||||||
|
};
|
||||||
|
|
||||||
|
var container = document.getElementById("editor");
|
||||||
|
var editor = new Editor(new Renderer(container, theme));
|
||||||
|
onDocChange();
|
||||||
|
|
||||||
|
window.jump = function() {
|
||||||
|
var jump = document.getElementById("jump")
|
||||||
|
var cursor = editor.getCursorPosition()
|
||||||
|
var pos = editor.renderer.textToScreenCoordinates(cursor.row, cursor.column);
|
||||||
|
jump.style.left = pos.pageX + "px";
|
||||||
|
jump.style.top = pos.pageY + "px";
|
||||||
|
jump.style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
function onResize() {
|
||||||
|
container.style.width = (document.documentElement.clientWidth - 4) + "px";
|
||||||
|
container.style.height = (document.documentElement.clientHeight - 55 - 4) + "px";
|
||||||
|
editor.resize();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onresize = onResize;
|
||||||
|
onResize();
|
||||||
|
|
||||||
|
event.addListener(container, "dragover", function(e) {
|
||||||
|
return event.preventDefault(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
event.addListener(container, "drop", function(e) {
|
||||||
|
try {
|
||||||
|
var file = e.dataTransfer.files[0];
|
||||||
|
} catch(e) {
|
||||||
|
return event.stopEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.FileReader) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function(e) {
|
||||||
|
editor.getSelection().selectAll();
|
||||||
|
|
||||||
|
var mode = "text";
|
||||||
|
if (/^.*\.js$/i.test(file.name)) {
|
||||||
|
mode = "javascript";
|
||||||
|
} else if (/^.*\.xml$/i.test(file.name)) {
|
||||||
|
mode = "xml";
|
||||||
|
} else if (/^.*\.html$/i.test(file.name)) {
|
||||||
|
mode = "html";
|
||||||
|
} else if (/^.*\.css$/i.test(file.name)) {
|
||||||
|
mode = "css";
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.onTextInput(reader.result);
|
||||||
|
|
||||||
|
modeEl.value = mode;
|
||||||
|
editor.getDocument().setMode(modes[mode]);
|
||||||
|
}
|
||||||
|
reader.readAsText(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return event.preventDefault(e);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
206
editor.html
206
editor.html
|
|
@ -48,7 +48,27 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
<script>
|
||||||
|
require = {
|
||||||
|
urlArgs: "bust=" + (new Date()).getTime()
|
||||||
|
};
|
||||||
|
</script>
|
||||||
<script src="demo/require.js" type="text/javascript" charset="utf-8"></script>
|
<script src="demo/require.js" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="demo/boot.js" type="text/javascript"></script>
|
||||||
|
<script>
|
||||||
|
setupPlugins({
|
||||||
|
pluginDirs: {
|
||||||
|
"../demo": {
|
||||||
|
singleFiles: ["demo_startup"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, function(plugin_manager) {
|
||||||
|
plugin_manager.catalog.startupPlugins({}, plugin_manager.REASONS.APP_STARTUP).then(function() {
|
||||||
|
var demo_startup = require("demo_startup");
|
||||||
|
demo_startup.launch();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="jump"></div>
|
<div id="jump"></div>
|
||||||
|
|
@ -131,191 +151,5 @@
|
||||||
</html>
|
</html>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript" charset="utf-8">
|
|
||||||
|
|
||||||
require(
|
|
||||||
{ baseUrl: "lib" },
|
|
||||||
[
|
|
||||||
"ace/lib/event",
|
|
||||||
"ace/editor",
|
|
||||||
"ace/virtual_renderer",
|
|
||||||
"ace/theme/textmate",
|
|
||||||
"ace/document",
|
|
||||||
"ace/mode/javascript",
|
|
||||||
"ace/mode/css",
|
|
||||||
"ace/mode/html",
|
|
||||||
"ace/mode/xml",
|
|
||||||
"ace/mode/text",
|
|
||||||
"ace/undomanager"
|
|
||||||
], function(
|
|
||||||
eventMod,
|
|
||||||
editorMod,
|
|
||||||
renderMod,
|
|
||||||
theme,
|
|
||||||
docMod,
|
|
||||||
jsMod,
|
|
||||||
cssMod,
|
|
||||||
htmlMod,
|
|
||||||
xmlMod,
|
|
||||||
textMod,
|
|
||||||
undoMod
|
|
||||||
) {
|
|
||||||
|
|
||||||
var event = eventMod.event;
|
|
||||||
var Editor = editorMod.Editor;
|
|
||||||
var Renderer = renderMod.VirtualRenderer;
|
|
||||||
var Document = docMod.Document;
|
|
||||||
var JavaScriptMode = jsMod.JavaScript;
|
|
||||||
var CssMode = cssMod.Css;
|
|
||||||
var HtmlMode = htmlMod.Html;
|
|
||||||
var XmlMode = xmlMod.Xml;
|
|
||||||
var TextMode = textMod.Text;
|
|
||||||
var UndoManager = undoMod.UndoManager;
|
|
||||||
|
|
||||||
var docs = {}
|
|
||||||
|
|
||||||
docs.js = new Document(document.getElementById("jstext").innerHTML);
|
|
||||||
docs.js.setMode(new JavaScriptMode());
|
|
||||||
docs.js.setUndoManager(new UndoManager());
|
|
||||||
|
|
||||||
docs.css = new Document(document.getElementById("csstext").innerHTML);
|
|
||||||
docs.css.setMode(new CssMode());
|
|
||||||
docs.css.setUndoManager(new UndoManager());
|
|
||||||
|
|
||||||
docs.html = new Document(document.getElementById("htmltext").innerHTML);
|
|
||||||
docs.html.setMode(new HtmlMode());
|
|
||||||
docs.html.setUndoManager(new UndoManager());
|
|
||||||
|
|
||||||
var docEl = document.getElementById("doc");
|
|
||||||
|
|
||||||
function onDocChange() {
|
|
||||||
var doc = getDoc();
|
|
||||||
editor.setDocument(doc);
|
|
||||||
|
|
||||||
var mode = doc.getMode();
|
|
||||||
if (mode instanceof JavaScriptMode) {
|
|
||||||
modeEl.value = "javascript"
|
|
||||||
}
|
|
||||||
else if (mode instanceof CssMode) {
|
|
||||||
modeEl.value = "css"
|
|
||||||
}
|
|
||||||
else if (mode instanceof HtmlMode) {
|
|
||||||
modeEl.value = "html"
|
|
||||||
}
|
|
||||||
else if (mode instanceof XmlMode) {
|
|
||||||
modeEl.value = "xml"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
modeEl.value = "text"
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.focus();
|
|
||||||
}
|
|
||||||
docEl.onchange = onDocChange;
|
|
||||||
|
|
||||||
function getDoc() {
|
|
||||||
return docs[docEl.value];
|
|
||||||
}
|
|
||||||
|
|
||||||
var modeEl = document.getElementById("mode");
|
|
||||||
modeEl.onchange = function() {
|
|
||||||
editor.getDocument().setMode(modes[modeEl.value] || modes.text);
|
|
||||||
};
|
|
||||||
|
|
||||||
var modes = {
|
|
||||||
text: new TextMode(),
|
|
||||||
xml: new XmlMode(),
|
|
||||||
html: new HtmlMode(),
|
|
||||||
css: new CssMode(),
|
|
||||||
javascript: new JavaScriptMode()
|
|
||||||
};
|
|
||||||
|
|
||||||
function getMode() {
|
|
||||||
return modes[modeEl.value];
|
|
||||||
}
|
|
||||||
|
|
||||||
var themeEl = document.getElementById("theme");
|
|
||||||
themeEl.onchange = function() {
|
|
||||||
editor.setTheme(themeEl.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
var selectEl = document.getElementById("select_style");
|
|
||||||
selectEl.onchange = function() {
|
|
||||||
if (selectEl.checked) {
|
|
||||||
editor.setSelectionStyle("line");
|
|
||||||
} else {
|
|
||||||
editor.setSelectionStyle("text");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var activeEl = document.getElementById("highlight_active");
|
|
||||||
activeEl.onchange = function() {
|
|
||||||
editor.setHighlightActiveLine(!!activeEl.checked);
|
|
||||||
};
|
|
||||||
|
|
||||||
var container = document.getElementById("editor");
|
|
||||||
var editor = new Editor(new Renderer(container, theme));
|
|
||||||
onDocChange();
|
|
||||||
|
|
||||||
window.jump = function() {
|
|
||||||
var jump = document.getElementById("jump")
|
|
||||||
var cursor = editor.getCursorPosition()
|
|
||||||
var pos = editor.renderer.textToScreenCoordinates(cursor.row, cursor.column);
|
|
||||||
jump.style.left = pos.pageX + "px";
|
|
||||||
jump.style.top = pos.pageY + "px";
|
|
||||||
jump.style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
function onResize() {
|
|
||||||
container.style.width = (document.documentElement.clientWidth - 4) + "px";
|
|
||||||
container.style.height = (document.documentElement.clientHeight - 55 - 4) + "px";
|
|
||||||
editor.resize();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onresize = onResize;
|
|
||||||
onResize();
|
|
||||||
|
|
||||||
event.addListener(container, "dragover", function(e) {
|
|
||||||
return event.preventDefault(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
event.addListener(container, "drop", function(e) {
|
|
||||||
try {
|
|
||||||
var file = e.dataTransfer.files[0];
|
|
||||||
} catch(e) {
|
|
||||||
return event.stopEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.FileReader) {
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onload = function(e) {
|
|
||||||
editor.getSelection().selectAll();
|
|
||||||
|
|
||||||
var mode = "text";
|
|
||||||
if (/^.*\.js$/i.test(file.name)) {
|
|
||||||
mode = "javascript";
|
|
||||||
} else if (/^.*\.xml$/i.test(file.name)) {
|
|
||||||
mode = "xml";
|
|
||||||
} else if (/^.*\.html$/i.test(file.name)) {
|
|
||||||
mode = "html";
|
|
||||||
} else if (/^.*\.css$/i.test(file.name)) {
|
|
||||||
mode = "css";
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.onTextInput(reader.result);
|
|
||||||
|
|
||||||
modeEl.value = mode;
|
|
||||||
editor.getDocument().setMode(modes[mode]);
|
|
||||||
}
|
|
||||||
reader.readAsText(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
return event.preventDefault(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
76
plugins/pilot/lib/console.js
Normal file
76
plugins/pilot/lib/console.js
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (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.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Skywriter.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Joe Walker (jwalker@mozilla.com)
|
||||||
|
* Patrick Walton (pwalton@mozilla.com)
|
||||||
|
* Julian Viereck (jviereck@mozilla.com)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object represents a "safe console" object that forwards debugging
|
||||||
|
* messages appropriately without creating a dependency on Firebug in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var noop = function() {};
|
||||||
|
|
||||||
|
// These are the functions that are available in Chrome 4/5, Safari 4
|
||||||
|
// and Firefox 3.6. Don't add to this list without checking browser support
|
||||||
|
var NAMES = [
|
||||||
|
"assert", "count", "debug", "dir", "dirxml", "error", "group", "groupEnd",
|
||||||
|
"info", "log", "profile", "profileEnd", "time", "timeEnd", "trace", "warn"
|
||||||
|
];
|
||||||
|
|
||||||
|
if (typeof(window) === 'undefined') {
|
||||||
|
// We're in a web worker. Forward to the main thread so the messages
|
||||||
|
// will show up.
|
||||||
|
NAMES.forEach(function(name) {
|
||||||
|
exports[name] = function() {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
var msg = { op: 'log', method: name, args: args };
|
||||||
|
postMessage(JSON.stringify(msg));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// For each of the console functions, copy them if they exist, stub if not
|
||||||
|
NAMES.forEach(function(name) {
|
||||||
|
if (window.console && window.console[name]) {
|
||||||
|
exports[name] = window.console[name].bind(window.console);
|
||||||
|
} else {
|
||||||
|
exports[name] = noop;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
147
plugins/pilot/lib/index.js
Normal file
147
plugins/pilot/lib/index.js
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (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.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Skywriter.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Kevin Dangoor (kdangoor@mozilla.com)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
exports.startup = function(data, reason) {
|
||||||
|
// Narwhal's shim for ES5 defineProperty
|
||||||
|
// ES5 15.2.3.6
|
||||||
|
if (!Object.defineProperty) {
|
||||||
|
Object.defineProperty = function(object, property, descriptor) {
|
||||||
|
var has = Object.prototype.hasOwnProperty;
|
||||||
|
if (typeof descriptor == "object" && object.__defineGetter__) {
|
||||||
|
if (has.call(descriptor, "value")) {
|
||||||
|
if (!object.__lookupGetter__(property) && !object.__lookupSetter__(property)) {
|
||||||
|
// data property defined and no pre-existing accessors
|
||||||
|
object[property] = descriptor.value;
|
||||||
|
}
|
||||||
|
if (has.call(descriptor, "get") || has.call(descriptor, "set")) {
|
||||||
|
// descriptor has a value property but accessor already exists
|
||||||
|
throw new TypeError("Object doesn't support this action");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fail silently if "writable", "enumerable", or "configurable"
|
||||||
|
// are requested but not supported
|
||||||
|
/*
|
||||||
|
// alternate approach:
|
||||||
|
if ( // can't implement these features; allow false but not true
|
||||||
|
!(has.call(descriptor, "writable") ? descriptor.writable : true) ||
|
||||||
|
!(has.call(descriptor, "enumerable") ? descriptor.enumerable : true) ||
|
||||||
|
!(has.call(descriptor, "configurable") ? descriptor.configurable : true)
|
||||||
|
)
|
||||||
|
throw new RangeError(
|
||||||
|
"This implementation of Object.defineProperty does not " +
|
||||||
|
"support configurable, enumerable, or writable."
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
else if (typeof descriptor.get == "function") {
|
||||||
|
object.__defineGetter__(property, descriptor.get);
|
||||||
|
}
|
||||||
|
if (typeof descriptor.set == "function") {
|
||||||
|
object.__defineSetter__(property, descriptor.set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ES5 15.2.3.7
|
||||||
|
if (!Object.defineProperties) {
|
||||||
|
Object.defineProperties = function(object, properties) {
|
||||||
|
for (var property in properties) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(properties, property)) {
|
||||||
|
Object.defineProperty(object, property, properties[property]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array detector.
|
||||||
|
* Firefox 3.5 and Safari 4 have this already. Chrome 4 however ...
|
||||||
|
* Note to Dojo - your isArray is still broken: instanceof doesn't work with
|
||||||
|
* Arrays taken from a different frame/window.
|
||||||
|
*/
|
||||||
|
if (!Array.isArray) {
|
||||||
|
Array.isArray = function(data) {
|
||||||
|
return data && Object.prototype.toString.call(data) === "[object Array]";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the list of keys on an object.
|
||||||
|
*/
|
||||||
|
if (!Object.keys) {
|
||||||
|
Object.keys = function(obj) {
|
||||||
|
var k, ret = [];
|
||||||
|
for (k in obj) {
|
||||||
|
if (obj.hasOwnProperty(k)) {
|
||||||
|
ret.push(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Function.prototype.bind) {
|
||||||
|
// From Narwhal
|
||||||
|
Function.prototype.bind = function () {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
var self = this;
|
||||||
|
var bound = function () {
|
||||||
|
return self.call.apply(
|
||||||
|
self,
|
||||||
|
args.concat(
|
||||||
|
Array.prototype.slice.call(arguments)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
bound.name = this.name;
|
||||||
|
bound.displayName = this.displayName;
|
||||||
|
bound.length = this.length;
|
||||||
|
bound.unbound = self;
|
||||||
|
return bound;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.globalsLoaded = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
149
plugins/pilot/lib/plugin_manager.js
Normal file
149
plugins/pilot/lib/plugin_manager.js
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (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.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Skywriter.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Kevin Dangoor (kdangoor@mozilla.com)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
var Promise = require("pilot/promise").Promise;
|
||||||
|
|
||||||
|
exports.REASONS = {
|
||||||
|
APP_STARTUP: 1,
|
||||||
|
APP_SHUTDOWN: 2,
|
||||||
|
PLUGIN_ENABLE: 3,
|
||||||
|
PLUGIN_DISABLE: 4,
|
||||||
|
PLUGIN_INSTALL: 5,
|
||||||
|
PLUGIN_UNINSTALL: 6,
|
||||||
|
PLUGIN_UPGRADE: 7,
|
||||||
|
PLUGIN_DOWNGRADE: 8
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Plugin = function(name) {
|
||||||
|
this.name = name;
|
||||||
|
this.status = this.INSTALLED;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Plugin.prototype = {
|
||||||
|
/**
|
||||||
|
* constants for the state
|
||||||
|
*/
|
||||||
|
NEW: 0,
|
||||||
|
INSTALLED: 1,
|
||||||
|
STARTED: 2,
|
||||||
|
SHUTDOWN: 3,
|
||||||
|
|
||||||
|
install: function(data, reason) {
|
||||||
|
var pr = new Promise();
|
||||||
|
if (this.status > this.NEW) {
|
||||||
|
pr.resolve(this);
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
require([this.name], function(pluginModule) {
|
||||||
|
if (pluginModule.install) {
|
||||||
|
pluginModule.install(data, reason);
|
||||||
|
}
|
||||||
|
this.status = this.INSTALLED;
|
||||||
|
pr.resolve(this);
|
||||||
|
}.bind(this));
|
||||||
|
return pr;
|
||||||
|
},
|
||||||
|
|
||||||
|
startup: function(data, reason) {
|
||||||
|
var pr = new Promise();
|
||||||
|
if (this.status != this.INSTALLED) {
|
||||||
|
pr.resolve(this);
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
require([this.name], function(pluginModule) {
|
||||||
|
if (pluginModule.startup) {
|
||||||
|
pluginModule.startup(data, reason);
|
||||||
|
}
|
||||||
|
this.status = this.STARTED;
|
||||||
|
pr.resolve(this);
|
||||||
|
}.bind(this));
|
||||||
|
return pr;
|
||||||
|
},
|
||||||
|
|
||||||
|
shutdown: function(data, reason) {
|
||||||
|
if (this.status != this.STARTED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pluginModule = require(this.name);
|
||||||
|
if (pluginModule.shutdown) {
|
||||||
|
pluginModule.shutdown(data, reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.PluginCatalog = function() {
|
||||||
|
this.plugins = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.PluginCatalog.prototype = {
|
||||||
|
registerPlugins: function(pluginList) {
|
||||||
|
pluginList.forEach(function(pluginName) {
|
||||||
|
var plugin = this.plugins[pluginName];
|
||||||
|
if (plugin === undefined) {
|
||||||
|
plugin = new exports.Plugin(pluginName);
|
||||||
|
this.plugins[pluginName] = plugin;
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
startupPlugins: function(data, reason) {
|
||||||
|
var startupPromises = [];
|
||||||
|
for (var pluginName in this.plugins) {
|
||||||
|
var plugin = this.plugins[pluginName];
|
||||||
|
startupPromises.push(plugin.startup(data, reason));
|
||||||
|
}
|
||||||
|
return Promise.group(startupPromises);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.catalog = new exports.PluginCatalog();
|
||||||
|
|
||||||
|
// TODO the code below is temporary to bootstrap while setting up the new command system
|
||||||
|
var PluginManager = {
|
||||||
|
commands : {},
|
||||||
|
|
||||||
|
registerCommand : function(name, command) {
|
||||||
|
this.commands[name] = command;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.PluginManager = PluginManager;
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
264
plugins/pilot/lib/promise.js
Normal file
264
plugins/pilot/lib/promise.js
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (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.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Skywriter.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Joe Walker (jwalker@mozilla.com)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
var console = require("pilot/console");
|
||||||
|
var Trace = require('pilot/stacktrace').Trace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A promise can be in one of 2 states.
|
||||||
|
* The ERROR and SUCCESS states are terminal, the PENDING state is the only
|
||||||
|
* start state.
|
||||||
|
*/
|
||||||
|
var ERROR = -1;
|
||||||
|
var PENDING = 0;
|
||||||
|
var SUCCESS = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We give promises and ID so we can track which are outstanding
|
||||||
|
*/
|
||||||
|
var _nextId = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debugging help if 2 things try to complete the same promise.
|
||||||
|
* This can be slow (especially on chrome due to the stack trace unwinding) so
|
||||||
|
* we should leave this turned off in normal use.
|
||||||
|
*/
|
||||||
|
var _traceCompletion = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outstanding promises. Handy list for debugging only.
|
||||||
|
*/
|
||||||
|
var _outstanding = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recently resolved promises. Also for debugging only.
|
||||||
|
*/
|
||||||
|
var _recent = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an unfulfilled promise
|
||||||
|
*/
|
||||||
|
Promise = function () {
|
||||||
|
this._status = PENDING;
|
||||||
|
this._value = undefined;
|
||||||
|
this._onSuccessHandlers = [];
|
||||||
|
this._onErrorHandlers = [];
|
||||||
|
|
||||||
|
// Debugging help
|
||||||
|
this._id = _nextId++;
|
||||||
|
//this._createTrace = new Trace(new Error());
|
||||||
|
_outstanding[this._id] = this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yeay for RTTI.
|
||||||
|
*/
|
||||||
|
Promise.prototype.isPromise = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have we either been resolve()ed or reject()ed?
|
||||||
|
*/
|
||||||
|
Promise.prototype.isComplete = function() {
|
||||||
|
return this._status != PENDING;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have we resolve()ed?
|
||||||
|
*/
|
||||||
|
Promise.prototype.isResolved = function() {
|
||||||
|
return this._status == SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Have we reject()ed?
|
||||||
|
*/
|
||||||
|
Promise.prototype.isRejected = function() {
|
||||||
|
return this._status == ERROR;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take the specified action of fulfillment of a promise, and (optionally)
|
||||||
|
* a different action on promise rejection.
|
||||||
|
*/
|
||||||
|
Promise.prototype.then = function(onSuccess, onError) {
|
||||||
|
if (typeof onSuccess === 'function') {
|
||||||
|
if (this._status === SUCCESS) {
|
||||||
|
onSuccess.call(null, this._value);
|
||||||
|
} else if (this._status === PENDING) {
|
||||||
|
this._onSuccessHandlers.push(onSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof onError === 'function') {
|
||||||
|
if (this._status === ERROR) {
|
||||||
|
onError.call(null, this._value);
|
||||||
|
} else if (this._status === PENDING) {
|
||||||
|
this._onErrorHandlers.push(onError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like then() except that rather than returning <tt>this</tt> we return
|
||||||
|
* a promise which
|
||||||
|
*/
|
||||||
|
Promise.prototype.chainPromise = function(onSuccess) {
|
||||||
|
var chain = new Promise();
|
||||||
|
chain._chainedFrom = this;
|
||||||
|
this.then(function(data) {
|
||||||
|
try {
|
||||||
|
chain.resolve(onSuccess(data));
|
||||||
|
} catch (ex) {
|
||||||
|
chain.reject(ex);
|
||||||
|
}
|
||||||
|
}, function(ex) {
|
||||||
|
chain.reject(ex);
|
||||||
|
});
|
||||||
|
return chain;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supply the fulfillment of a promise
|
||||||
|
*/
|
||||||
|
Promise.prototype.resolve = function(data) {
|
||||||
|
return this._complete(this._onSuccessHandlers, SUCCESS, data, 'resolve');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renege on a promise
|
||||||
|
*/
|
||||||
|
Promise.prototype.reject = function(data) {
|
||||||
|
return this._complete(this._onErrorHandlers, ERROR, data, 'reject');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method to be called on resolve() or reject().
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Promise.prototype._complete = function(list, status, data, name) {
|
||||||
|
// Complain if we've already been completed
|
||||||
|
if (this._status != PENDING) {
|
||||||
|
console.group('Promise already closed');
|
||||||
|
console.error('Attempted ' + name + '() with ', data);
|
||||||
|
console.error('Previous status = ', this._status,
|
||||||
|
', previous value = ', this._value);
|
||||||
|
console.trace();
|
||||||
|
|
||||||
|
if (this._completeTrace) {
|
||||||
|
console.error('Trace of previous completion:');
|
||||||
|
this._completeTrace.log(5);
|
||||||
|
}
|
||||||
|
console.groupEnd();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_traceCompletion) {
|
||||||
|
this._completeTrace = new Trace(new Error());
|
||||||
|
}
|
||||||
|
|
||||||
|
this._status = status;
|
||||||
|
this._value = data;
|
||||||
|
|
||||||
|
// Call all the handlers, and then delete them
|
||||||
|
list.forEach(function(handler) {
|
||||||
|
handler.call(null, this._value);
|
||||||
|
}, this);
|
||||||
|
this._onSuccessHandlers.length = 0;
|
||||||
|
this._onErrorHandlers.length = 0;
|
||||||
|
|
||||||
|
// Remove the given {promise} from the _outstanding list, and add it to the
|
||||||
|
// _recent list, pruning more than 20 recent promises from that list.
|
||||||
|
delete _outstanding[this._id];
|
||||||
|
_recent.push(this);
|
||||||
|
while (_recent.length > 20) {
|
||||||
|
_recent.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of promises and returns a promise that that is fulfilled once
|
||||||
|
* all the promises in the array are fulfilled
|
||||||
|
* @param group The array of promises
|
||||||
|
* @return the promise that is fulfilled when all the array is fulfilled
|
||||||
|
*/
|
||||||
|
Promise.group = function(promiseList) {
|
||||||
|
if (!(promiseList instanceof Array)) {
|
||||||
|
promiseList = Array.prototype.slice.call(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the original array has nothing in it, return now to avoid waiting
|
||||||
|
if (promiseList.length === 0) {
|
||||||
|
return new Promise().resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupPromise = new Promise();
|
||||||
|
var results = [];
|
||||||
|
var fulfilled = 0;
|
||||||
|
|
||||||
|
var onSuccessFactory = function(index) {
|
||||||
|
return function(data) {
|
||||||
|
results[index] = data;
|
||||||
|
fulfilled++;
|
||||||
|
// If the group has already failed, silently drop extra results
|
||||||
|
if (groupPromise._status !== ERROR) {
|
||||||
|
if (fulfilled === promiseList.length) {
|
||||||
|
groupPromise.resolve(results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
promiseList.forEach(function(promise, index) {
|
||||||
|
var onSuccess = onSuccessFactory(index);
|
||||||
|
var onError = groupPromise.reject.bind(groupPromise);
|
||||||
|
promise.then(onSuccess, onError);
|
||||||
|
});
|
||||||
|
|
||||||
|
return groupPromise;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Promise = Promise;
|
||||||
|
exports._outstanding = _outstanding;
|
||||||
|
exports._recent = _recent;
|
||||||
|
|
||||||
|
});
|
||||||
82
plugins/pilot/lib/proxy.js
Normal file
82
plugins/pilot/lib/proxy.js
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (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.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Skywriter.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Julian Viereck (jviereck@mozilla.com)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
var Promise = require('pilot/promise').Promise;
|
||||||
|
|
||||||
|
exports.xhr = function(method, url, async, beforeSendCallback) {
|
||||||
|
var pr = new Promise();
|
||||||
|
|
||||||
|
if (!skywriter.proxy || !skywriter.proxy.xhr) {
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.onreadystatechange = function() {
|
||||||
|
if (req.readyState !== 4) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var status = req.status;
|
||||||
|
if (status !== 0 && status !== 200) {
|
||||||
|
var error = new Error(req.responseText + ' (Status ' + req.status + ")");
|
||||||
|
error.xhr = req;
|
||||||
|
pr.reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr.resolve(req.responseText);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
req.open("GET", url, async);
|
||||||
|
if (beforeSendCallback) {
|
||||||
|
beforeSendCallback(req);
|
||||||
|
}
|
||||||
|
req.send();
|
||||||
|
} else {
|
||||||
|
skywriter.proxy.xhr.call(this, method, url, async, beforeSendCallback, pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pr;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Worker = function(url) {
|
||||||
|
if (!skywriter.proxy || !skywriter.proxy.worker) {
|
||||||
|
return new Worker(url);
|
||||||
|
} else {
|
||||||
|
return new skywriter.proxy.worker(url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
332
plugins/pilot/lib/stacktrace.js
Normal file
332
plugins/pilot/lib/stacktrace.js
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
var util = require("pilot/util");
|
||||||
|
var console = require('pilot/console');
|
||||||
|
|
||||||
|
// Changed to suit the specific needs of running within Skywriter
|
||||||
|
|
||||||
|
// Domain Public by Eric Wendelin http://eriwen.com/ (2008)
|
||||||
|
// Luke Smith http://lucassmith.name/ (2008)
|
||||||
|
// Loic Dachary <loic@dachary.org> (2008)
|
||||||
|
// Johan Euphrosine <proppy@aminche.com> (2008)
|
||||||
|
// Øyvind Sean Kinsey http://kinsey.no/blog
|
||||||
|
//
|
||||||
|
// Information and discussions
|
||||||
|
// http://jspoker.pokersource.info/skin/test-printstacktrace.html
|
||||||
|
// http://eriwen.com/javascript/js-stack-trace/
|
||||||
|
// http://eriwen.com/javascript/stacktrace-update/
|
||||||
|
// http://pastie.org/253058
|
||||||
|
// http://browsershots.org/http://jspoker.pokersource.info/skin/test-printstacktrace.html
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// guessFunctionNameFromLines comes from firebug
|
||||||
|
//
|
||||||
|
// Software License Agreement (BSD License)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2007, Parakey Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use of this software in source and binary forms, with or without modification,
|
||||||
|
// are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above
|
||||||
|
// copyright notice, this list of conditions and the
|
||||||
|
// following disclaimer.
|
||||||
|
//
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the
|
||||||
|
// following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// * Neither the name of Parakey Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products
|
||||||
|
// derived from this software without specific prior
|
||||||
|
// written permission of Parakey Inc.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||||
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||||
|
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||||
|
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Different browsers create stack traces in different ways.
|
||||||
|
* <strike>Feature</strike> Browser detection baby ;).
|
||||||
|
*/
|
||||||
|
var mode = (function() {
|
||||||
|
|
||||||
|
// We use SC's browser detection here to avoid the "break on error"
|
||||||
|
// functionality provided by Firebug. Firebug tries to do the right
|
||||||
|
// thing here and break, but it happens every time you load the page.
|
||||||
|
// bug 554105
|
||||||
|
if (util.isMozilla) {
|
||||||
|
return 'firefox';
|
||||||
|
} else if (util.isOpera) {
|
||||||
|
return 'opera';
|
||||||
|
} else if (util.isSafari) {
|
||||||
|
return 'other';
|
||||||
|
}
|
||||||
|
|
||||||
|
// SC doesn't do any detection of Chrome at this time.
|
||||||
|
|
||||||
|
// this is the original feature detection code that is used as a
|
||||||
|
// fallback.
|
||||||
|
try {
|
||||||
|
(0)();
|
||||||
|
} catch (e) {
|
||||||
|
if (e.arguments) {
|
||||||
|
return 'chrome';
|
||||||
|
}
|
||||||
|
if (e.stack) {
|
||||||
|
return 'firefox';
|
||||||
|
}
|
||||||
|
if (window.opera && !('stacktrace' in e)) { //Opera 9-
|
||||||
|
return 'opera';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'other';
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function stringifyArguments(args) {
|
||||||
|
for (var i = 0; i < args.length; ++i) {
|
||||||
|
var argument = args[i];
|
||||||
|
if (typeof argument == 'object') {
|
||||||
|
args[i] = '#object';
|
||||||
|
} else if (typeof argument == 'function') {
|
||||||
|
args[i] = '#function';
|
||||||
|
} else if (typeof argument == 'string') {
|
||||||
|
args[i] = '"' + argument + '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract a stack trace from the format emitted by each browser.
|
||||||
|
*/
|
||||||
|
var decoders = {
|
||||||
|
chrome: function(e) {
|
||||||
|
var stack = e.stack;
|
||||||
|
if (!stack) {
|
||||||
|
console.log(e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return stack.replace(/^.*?\n/, '').
|
||||||
|
replace(/^.*?\n/, '').
|
||||||
|
replace(/^.*?\n/, '').
|
||||||
|
replace(/^[^\(]+?[\n$]/gm, '').
|
||||||
|
replace(/^\s+at\s+/gm, '').
|
||||||
|
replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@').
|
||||||
|
split('\n');
|
||||||
|
},
|
||||||
|
|
||||||
|
firefox: function(e) {
|
||||||
|
var stack = e.stack;
|
||||||
|
if (!stack) {
|
||||||
|
console.log(e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
// stack = stack.replace(/^.*?\n/, '');
|
||||||
|
stack = stack.replace(/(?:\n@:0)?\s+$/m, '');
|
||||||
|
stack = stack.replace(/^\(/gm, '{anonymous}(');
|
||||||
|
return stack.split('\n');
|
||||||
|
},
|
||||||
|
|
||||||
|
// Opera 7.x and 8.x only!
|
||||||
|
opera: function(e) {
|
||||||
|
var lines = e.message.split('\n'), ANON = '{anonymous}',
|
||||||
|
lineRE = /Line\s+(\d+).*?script\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i, i, j, len;
|
||||||
|
|
||||||
|
for (i = 4, j = 0, len = lines.length; i < len; i += 2) {
|
||||||
|
if (lineRE.test(lines[i])) {
|
||||||
|
lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + '()@' + RegExp.$2 + ':' + RegExp.$1) +
|
||||||
|
' -- ' +
|
||||||
|
lines[i + 1].replace(/^\s+/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.splice(j, lines.length - j);
|
||||||
|
return lines;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Safari, Opera 9+, IE, and others
|
||||||
|
other: function(curr) {
|
||||||
|
var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], j = 0, fn, args;
|
||||||
|
|
||||||
|
var maxStackSize = 10;
|
||||||
|
while (curr && stack.length < maxStackSize) {
|
||||||
|
fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
|
||||||
|
args = Array.prototype.slice.call(curr['arguments']);
|
||||||
|
stack[j++] = fn + '(' + stringifyArguments(args) + ')';
|
||||||
|
|
||||||
|
//Opera bug: if curr.caller does not exist, Opera returns curr (WTF)
|
||||||
|
if (curr === curr.caller && window.opera) {
|
||||||
|
//TODO: check for same arguments if possible
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
curr = curr.caller;
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function NameGuesser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
NameGuesser.prototype = {
|
||||||
|
|
||||||
|
sourceCache: {},
|
||||||
|
|
||||||
|
ajax: function(url) {
|
||||||
|
var req = this.createXMLHTTPObject();
|
||||||
|
if (!req) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.open('GET', url, false);
|
||||||
|
req.setRequestHeader('User-Agent', 'XMLHTTP/1.0');
|
||||||
|
req.send('');
|
||||||
|
return req.responseText;
|
||||||
|
},
|
||||||
|
|
||||||
|
createXMLHTTPObject: function() {
|
||||||
|
// Try XHR methods in order and store XHR factory
|
||||||
|
var xmlhttp, XMLHttpFactories = [
|
||||||
|
function() {
|
||||||
|
return new XMLHttpRequest();
|
||||||
|
}, function() {
|
||||||
|
return new ActiveXObject('Msxml2.XMLHTTP');
|
||||||
|
}, function() {
|
||||||
|
return new ActiveXObject('Msxml3.XMLHTTP');
|
||||||
|
}, function() {
|
||||||
|
return new ActiveXObject('Microsoft.XMLHTTP');
|
||||||
|
}
|
||||||
|
];
|
||||||
|
for (var i = 0; i < XMLHttpFactories.length; i++) {
|
||||||
|
try {
|
||||||
|
xmlhttp = XMLHttpFactories[i]();
|
||||||
|
// Use memoization to cache the factory
|
||||||
|
this.createXMLHTTPObject = XMLHttpFactories[i];
|
||||||
|
return xmlhttp;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getSource: function(url) {
|
||||||
|
if (!(url in this.sourceCache)) {
|
||||||
|
this.sourceCache[url] = this.ajax(url).split('\n');
|
||||||
|
}
|
||||||
|
return this.sourceCache[url];
|
||||||
|
},
|
||||||
|
|
||||||
|
guessFunctions: function(stack) {
|
||||||
|
for (var i = 0; i < stack.length; ++i) {
|
||||||
|
var reStack = /{anonymous}\(.*\)@(\w+:\/\/([-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/;
|
||||||
|
var frame = stack[i], m = reStack.exec(frame);
|
||||||
|
if (m) {
|
||||||
|
var file = m[1], lineno = m[4]; //m[7] is character position in Chrome
|
||||||
|
if (file && lineno) {
|
||||||
|
var functionName = this.guessFunctionName(file, lineno);
|
||||||
|
stack[i] = frame.replace('{anonymous}', functionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
},
|
||||||
|
|
||||||
|
guessFunctionName: function(url, lineNo) {
|
||||||
|
try {
|
||||||
|
return this.guessFunctionNameFromLines(lineNo, this.getSource(url));
|
||||||
|
} catch (e) {
|
||||||
|
return 'getSource failed with url: ' + url + ', exception: ' + e.toString();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
guessFunctionNameFromLines: function(lineNo, source) {
|
||||||
|
var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
|
||||||
|
var reGuessFunction = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
|
||||||
|
// Walk backwards from the first line in the function until we find the line which
|
||||||
|
// matches the pattern above, which is the function definition
|
||||||
|
var line = '', maxLines = 10;
|
||||||
|
for (var i = 0; i < maxLines; ++i) {
|
||||||
|
line = source[lineNo - i] + line;
|
||||||
|
if (line !== undefined) {
|
||||||
|
var m = reGuessFunction.exec(line);
|
||||||
|
if (m) {
|
||||||
|
return m[1];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m = reFunctionArgNames.exec(line);
|
||||||
|
}
|
||||||
|
if (m && m[1]) {
|
||||||
|
return m[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '(?)';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var guesser = new NameGuesser();
|
||||||
|
|
||||||
|
var frameIgnorePatterns = [
|
||||||
|
/http:\/\/localhost:4020\/sproutcore.js:/
|
||||||
|
];
|
||||||
|
|
||||||
|
exports.ignoreFramesMatching = function(regex) {
|
||||||
|
frameIgnorePatterns.push(regex);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a stack trace from an exception
|
||||||
|
* @param ex {Error} The error to create a stacktrace from (optional)
|
||||||
|
* @param guess {Boolean} If we should try to resolve the names of anonymous functions
|
||||||
|
*/
|
||||||
|
exports.Trace = function Trace(ex, guess) {
|
||||||
|
this._ex = ex;
|
||||||
|
this._stack = decoders[mode](ex);
|
||||||
|
|
||||||
|
if (guess) {
|
||||||
|
this._stack = guesser.guessFunctions(this._stack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log to the console a number of lines (default all of them)
|
||||||
|
* @param lines {number} Maximum number of lines to wrote to console
|
||||||
|
*/
|
||||||
|
exports.Trace.prototype.log = function(lines) {
|
||||||
|
if (lines <= 0) {
|
||||||
|
// You aren't going to have more lines in your stack trace than this
|
||||||
|
// and it still fits in a 32bit integer
|
||||||
|
lines = 999999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
var printed = 0;
|
||||||
|
for (var i = 0; i < this._stack.length && printed < lines; i++) {
|
||||||
|
var frame = this._stack[i];
|
||||||
|
var display = true;
|
||||||
|
frameIgnorePatterns.forEach(function(regex) {
|
||||||
|
if (regex.test(frame)) {
|
||||||
|
display = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (display) {
|
||||||
|
console.debug(frame);
|
||||||
|
printed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
659
plugins/pilot/lib/util.js
Normal file
659
plugins/pilot/lib/util.js
Normal file
|
|
@ -0,0 +1,659 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (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.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla Skywriter.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Mozilla.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Skywriter Team (skywriter@mozilla.com)
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
define(function(require, exports, module) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an object representing a de-serialized query section of a URL.
|
||||||
|
* Query keys with multiple values are returned in an array.
|
||||||
|
* <p>Example: The input "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&"
|
||||||
|
* Produces the output object:
|
||||||
|
* <pre>{
|
||||||
|
* foo: [ "bar", "baz" ],
|
||||||
|
* thinger: " spaces =blah",
|
||||||
|
* zonk: "blarg"
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* <p>Note that spaces and other urlencoded entities are correctly handled
|
||||||
|
* @see dojo.queryToObject()
|
||||||
|
* While dojo.queryToObject() is mainly for URL query strings, this version
|
||||||
|
* allows to specify a separator character
|
||||||
|
*/
|
||||||
|
exports.queryToObject = function(str, seperator) {
|
||||||
|
var ret = {};
|
||||||
|
var qp = str.split(seperator || "&");
|
||||||
|
var dec = decodeURIComponent;
|
||||||
|
qp.forEach(function(item) {
|
||||||
|
if (item.length) {
|
||||||
|
var parts = item.split("=");
|
||||||
|
var name = dec(parts.shift());
|
||||||
|
var val = dec(parts.join("="));
|
||||||
|
if (exports.isString(ret[name])){
|
||||||
|
ret[name] = [ret[name]];
|
||||||
|
}
|
||||||
|
if (Array.isArray(ret[name])){
|
||||||
|
ret[name].push(val);
|
||||||
|
} else {
|
||||||
|
ret[name] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a name/value mapping object and returns a string representing a
|
||||||
|
* URL-encoded version of that object for use in a GET request
|
||||||
|
* <p>For example, given the input:
|
||||||
|
* <code>{ blah: "blah", multi: [ "thud", "thonk" ] }</code>
|
||||||
|
* The following string would be returned:
|
||||||
|
* <code>"blah=blah&multi=thud&multi=thonk"</code>
|
||||||
|
* @param map {Object} The object to convert
|
||||||
|
* @return {string} A URL-encoded version of the input
|
||||||
|
*/
|
||||||
|
exports.objectToQuery = function(map) {
|
||||||
|
// FIXME: need to implement encodeAscii!!
|
||||||
|
var enc = encodeURIComponent;
|
||||||
|
var pairs = [];
|
||||||
|
var backstop = {};
|
||||||
|
for (var name in map) {
|
||||||
|
var value = map[name];
|
||||||
|
if (value != backstop[name]) {
|
||||||
|
var assign = enc(name) + "=";
|
||||||
|
if (value.isArray) {
|
||||||
|
for (var i = 0; i < value.length; i++) {
|
||||||
|
pairs.push(assign + enc(value[i]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pairs.push(assign + enc(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pairs.join("&");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the count to keep a unique value for setTimeout
|
||||||
|
* @private See rateLimit()
|
||||||
|
*/
|
||||||
|
var nextRateLimitId = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the timeouts so they can be cleared later
|
||||||
|
* @private See rateLimit()
|
||||||
|
*/
|
||||||
|
var rateLimitTimeouts = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delay calling some function to check that it's not called again inside a
|
||||||
|
* maxRate. The real function is called after maxRate ms unless the return
|
||||||
|
* value of this function is called before, in which case the clock is restarted
|
||||||
|
*/
|
||||||
|
exports.rateLimit = function(maxRate, scope, func) {
|
||||||
|
if (maxRate) {
|
||||||
|
var rateLimitId = nextRateLimitId++;
|
||||||
|
|
||||||
|
return function() {
|
||||||
|
if (rateLimitTimeouts[rateLimitId]) {
|
||||||
|
clearTimeout(rateLimitTimeouts[rateLimitId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rateLimitTimeouts[rateLimitId] = setTimeout(function() {
|
||||||
|
func.apply(scope, arguments);
|
||||||
|
delete rateLimitTimeouts[rateLimitId];
|
||||||
|
}, maxRate);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if it is a String
|
||||||
|
*/
|
||||||
|
exports.isString = function(it) {
|
||||||
|
return (typeof it == "string" || it instanceof String);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if it is a Boolean.
|
||||||
|
*/
|
||||||
|
exports.isBoolean = function(it) {
|
||||||
|
return (typeof it == 'boolean');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if it is a Number.
|
||||||
|
*/
|
||||||
|
exports.isNumber = function(it) {
|
||||||
|
return (typeof it == 'number' && isFinite(it));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hack copied from dojo.
|
||||||
|
*/
|
||||||
|
exports.isObject = function(it) {
|
||||||
|
return it !== undefined &&
|
||||||
|
(it === null || typeof it == "object" ||
|
||||||
|
Array.isArray(it) || exports.isFunction(it));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the passed object a function?
|
||||||
|
* From dojo.isFunction()
|
||||||
|
*/
|
||||||
|
exports.isFunction = (function() {
|
||||||
|
var _isFunction = function(it) {
|
||||||
|
var t = typeof it; // must evaluate separately due to bizarre Opera bug. See #8937
|
||||||
|
//Firefox thinks object HTML element is a function, so test for nodeType.
|
||||||
|
return it && (t == "function" || it instanceof Function) && !it.nodeType; // Boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
return exports.isSafari ?
|
||||||
|
// only slow this down w/ gratuitious casting in Safari (not WebKit)
|
||||||
|
function(/*anything*/ it) {
|
||||||
|
if (typeof it == "function" && it == "[object NodeList]") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _isFunction(it); // Boolean
|
||||||
|
} : _isFunction;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A la Prototype endsWith(). Takes a regex excluding the '$' end marker
|
||||||
|
*/
|
||||||
|
exports.endsWith = function(str, end) {
|
||||||
|
if (!str) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return str.match(new RegExp(end + "$"));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A la Prototype include().
|
||||||
|
*/
|
||||||
|
exports.include = function(array, item) {
|
||||||
|
return array.indexOf(item) > -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like include, but useful when you're checking for a specific
|
||||||
|
* property on each object in the list...
|
||||||
|
*
|
||||||
|
* Returns null if the item is not in the list, otherwise
|
||||||
|
* returns the index of the item.
|
||||||
|
*/
|
||||||
|
exports.indexOfProperty = function(array, propertyName, item) {
|
||||||
|
for (var i = 0; i < array.length; i++) {
|
||||||
|
if (array[i][propertyName] == item) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A la Prototype last().
|
||||||
|
*/
|
||||||
|
exports.last = function(array) {
|
||||||
|
if (Array.isArray(array)) {
|
||||||
|
return array[array.length - 1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Knock off any undefined items from the end of an array
|
||||||
|
*/
|
||||||
|
exports.shrinkArray = function(array) {
|
||||||
|
var newArray = [];
|
||||||
|
|
||||||
|
var stillAtBeginning = true;
|
||||||
|
array.reverse().forEach(function(item) {
|
||||||
|
if (stillAtBeginning && item === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stillAtBeginning = false;
|
||||||
|
|
||||||
|
newArray.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
return newArray.reverse();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array
|
||||||
|
* @param number The size of the new array to create
|
||||||
|
* @param character The item to put in the array, defaults to ' '
|
||||||
|
*/
|
||||||
|
exports.makeArray = function(number, character) {
|
||||||
|
if (number < 1) {
|
||||||
|
return []; // give us a normal number please!
|
||||||
|
}
|
||||||
|
if (!character){character = ' ';}
|
||||||
|
|
||||||
|
var newArray = [];
|
||||||
|
for (var i = 0; i < number; i++) {
|
||||||
|
newArray.push(character);
|
||||||
|
}
|
||||||
|
return newArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repeat a string a given number of times.
|
||||||
|
* @param string String to repeat
|
||||||
|
* @param repeat Number of times to repeat
|
||||||
|
*/
|
||||||
|
exports.repeatString = function(string, repeat) {
|
||||||
|
var newstring = '';
|
||||||
|
|
||||||
|
for (var i = 0; i < repeat; i++) {
|
||||||
|
newstring += string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newstring;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a row, find the number of leading spaces.
|
||||||
|
* E.g. an array with the string " aposjd" would return 2
|
||||||
|
* @param row The row to hunt through
|
||||||
|
*/
|
||||||
|
exports.leadingSpaces = function(row) {
|
||||||
|
var numspaces = 0;
|
||||||
|
for (var i = 0; i < row.length; i++) {
|
||||||
|
if (row[i] == ' ' || row[i] == '' || row[i] === undefined) {
|
||||||
|
numspaces++;
|
||||||
|
} else {
|
||||||
|
return numspaces;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numspaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a row, find the number of leading tabs.
|
||||||
|
* E.g. an array with the string " aposjd" would return 2
|
||||||
|
* @param row The row to hunt through
|
||||||
|
*/
|
||||||
|
exports.leadingTabs = function(row) {
|
||||||
|
var numtabs = 0;
|
||||||
|
for (var i = 0; i < row.length; i++) {
|
||||||
|
if (row[i] == ' ' || row[i] == '' || row[i] === undefined) {
|
||||||
|
numtabs++;
|
||||||
|
} else {
|
||||||
|
return numtabs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numtabs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a row, extract a copy of the leading spaces or tabs.
|
||||||
|
* E.g. an array with the string " aposjd" would return an array with the
|
||||||
|
* string " ".
|
||||||
|
* @param row The row to hunt through
|
||||||
|
*/
|
||||||
|
exports.leadingWhitespace = function(row) {
|
||||||
|
var leading = [];
|
||||||
|
for (var i = 0; i < row.length; i++) {
|
||||||
|
if (row[i] == ' ' || row[i] == ' ' || row[i] == '' || row[i] === undefined) {
|
||||||
|
leading.push(row[i]);
|
||||||
|
} else {
|
||||||
|
return leading;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return leading;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a camelCaseWord convert to "Camel Case Word"
|
||||||
|
*/
|
||||||
|
exports.englishFromCamel = function(camel) {
|
||||||
|
camel.replace(/([A-Z])/g, function(str) {
|
||||||
|
return " " + str.toLowerCase();
|
||||||
|
}).trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* I hate doing this, but we need some way to determine if the user is on a Mac
|
||||||
|
* The reason is that users have different expectations of their key combinations.
|
||||||
|
*
|
||||||
|
* Take copy as an example, Mac people expect to use CMD or APPLE + C
|
||||||
|
* Windows folks expect to use CTRL + C
|
||||||
|
*/
|
||||||
|
exports.OS = {
|
||||||
|
LINUX: 'LINUX',
|
||||||
|
MAC: 'MAC',
|
||||||
|
WINDOWS: 'WINDOWS'
|
||||||
|
};
|
||||||
|
|
||||||
|
var ua = navigator.userAgent;
|
||||||
|
var av = navigator.appVersion;
|
||||||
|
|
||||||
|
/** Is the user using a browser that identifies itself as Linux */
|
||||||
|
exports.isLinux = av.indexOf("Linux") >= 0;
|
||||||
|
|
||||||
|
/** Is the user using a browser that identifies itself as Windows */
|
||||||
|
exports.isWindows = av.indexOf("Win") >= 0;
|
||||||
|
|
||||||
|
/** Is the user using a browser that identifies itself as WebKit */
|
||||||
|
exports.isWebKit = parseFloat(ua.split("WebKit/")[1]) || undefined;
|
||||||
|
|
||||||
|
/** Is the user using a browser that identifies itself as Chrome */
|
||||||
|
exports.isChrome = parseFloat(ua.split("Chrome/")[1]) || undefined;
|
||||||
|
|
||||||
|
/** Is the user using a browser that identifies itself as Mac OS */
|
||||||
|
exports.isMac = av.indexOf("Macintosh") >= 0;
|
||||||
|
|
||||||
|
/* Is this Firefox or related? */
|
||||||
|
exports.isMozilla = av.indexOf('Gecko/') >= 0;
|
||||||
|
|
||||||
|
if (ua.indexOf("AdobeAIR") >= 0) {
|
||||||
|
exports.isAIR = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the user using a browser that identifies itself as Safari
|
||||||
|
* See also:
|
||||||
|
* - http://developer.apple.com/internet/safari/faq.html#anchor2
|
||||||
|
* - http://developer.apple.com/internet/safari/uamatrix.html
|
||||||
|
*/
|
||||||
|
var index = Math.max(av.indexOf("WebKit"), av.indexOf("Safari"), 0);
|
||||||
|
if (index && !exports.isChrome) {
|
||||||
|
// try to grab the explicit Safari version first. If we don't get
|
||||||
|
// one, look for less than 419.3 as the indication that we're on something
|
||||||
|
// "Safari 2-ish".
|
||||||
|
exports.isSafari = parseFloat(av.split("Version/")[1]);
|
||||||
|
if (!exports.isSafari || parseFloat(av.substr(index + 7)) <= 419.3) {
|
||||||
|
exports.isSafari = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ua.indexOf("Gecko") >= 0 && !exports.isWebKit) {
|
||||||
|
exports.isMozilla = parseFloat(av);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a exports.OS constant
|
||||||
|
*/
|
||||||
|
exports.getOS = function() {
|
||||||
|
if (exports.isMac) {
|
||||||
|
return exports.OS['MAC'];
|
||||||
|
} else if (exports.isLinux) {
|
||||||
|
return exports.OS['LINUX'];
|
||||||
|
} else {
|
||||||
|
return exports.OS['WINDOWS'];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Returns true if the DOM element "b" is inside the element "a". */
|
||||||
|
if (typeof(document) !== 'undefined' && document.compareDocumentPosition) {
|
||||||
|
exports.contains = function(a, b) {
|
||||||
|
return a.compareDocumentPosition(b) & 16;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
exports.contains = function(a, b) {
|
||||||
|
return a !== b && (a.contains ? a.contains(b) : true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevents propagation and clobbers the default action of the passed event
|
||||||
|
*/
|
||||||
|
exports.stopEvent = function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a random password of the given length (default 16 chars)
|
||||||
|
*/
|
||||||
|
exports.randomPassword = function(length) {
|
||||||
|
length = length || 16;
|
||||||
|
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||||
|
var pass = "";
|
||||||
|
for (var x = 0; x < length; x++) {
|
||||||
|
var charIndex = Math.floor(Math.random() * chars.length);
|
||||||
|
pass += chars.charAt(charIndex);
|
||||||
|
}
|
||||||
|
return pass;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the passed object free of members, i.e. are there any enumerable
|
||||||
|
* properties which the objects claims as it's own using hasOwnProperty()
|
||||||
|
*/
|
||||||
|
exports.isEmpty = function(object) {
|
||||||
|
for (var x in object) {
|
||||||
|
if (object.hasOwnProperty(x)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the name of a project indicate that it is owned by someone else
|
||||||
|
* TODO: This is a major hack. We really should have a File object that include
|
||||||
|
* separate owner information.
|
||||||
|
*/
|
||||||
|
exports.isMyProject = function(project) {
|
||||||
|
return project.indexOf("+") == -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a date as dd MMM yyyy
|
||||||
|
*/
|
||||||
|
exports.formatDate = function (date) {
|
||||||
|
if (!date) {
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
return date.getDate() + " " +
|
||||||
|
exports.formatDate.shortMonths[date.getMonth()] + " " +
|
||||||
|
date.getFullYear();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Month data for exports.formatDate
|
||||||
|
*/
|
||||||
|
exports.formatDate.shortMonths = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a CSS class to the list of classes on the given node
|
||||||
|
*/
|
||||||
|
exports.addClass = function(node, className) {
|
||||||
|
var parts = className.split(/\s+/);
|
||||||
|
var cls = " " + node.className + " ";
|
||||||
|
for (var i = 0, len = parts.length, c; i < len; ++i) {
|
||||||
|
c = parts[i];
|
||||||
|
if (c && cls.indexOf(" " + c + " ") < 0) {
|
||||||
|
cls += c + " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.className = cls.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a CSS class from the list of classes on the given node
|
||||||
|
*/
|
||||||
|
exports.removeClass = function(node, className) {
|
||||||
|
var cls;
|
||||||
|
if (className !== undefined) {
|
||||||
|
var parts = className.split(/\s+/);
|
||||||
|
cls = " " + node.className + " ";
|
||||||
|
for (var i = 0, len = parts.length; i < len; ++i) {
|
||||||
|
cls = cls.replace(" " + parts[i] + " ", " ");
|
||||||
|
}
|
||||||
|
cls = cls.trim();
|
||||||
|
} else {
|
||||||
|
cls = "";
|
||||||
|
}
|
||||||
|
if (node.className != cls) {
|
||||||
|
node.className = cls;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or remove a CSS class from the list of classes on the given node
|
||||||
|
* depending on the value of <tt>include</tt>
|
||||||
|
*/
|
||||||
|
exports.setClass = function(node, className, include) {
|
||||||
|
if (include) {
|
||||||
|
exports.addClass(node, className);
|
||||||
|
} else {
|
||||||
|
exports.removeClass(node, className);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the passed object either null or undefined (using ===)
|
||||||
|
*/
|
||||||
|
exports.none = function(obj) {
|
||||||
|
return obj === null || obj === undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a clone of the passed object. This function can take just about
|
||||||
|
* any type of object and create a clone of it, including primitive values
|
||||||
|
* (which are not actually cloned because they are immutable).
|
||||||
|
* If the passed object implements the clone() method, then this function
|
||||||
|
* will simply call that method and return the result.
|
||||||
|
*
|
||||||
|
* @param object {Object} the object to clone
|
||||||
|
* @param deep {Boolean} do a deep clone?
|
||||||
|
* @returns {Object} the cloned object
|
||||||
|
*/
|
||||||
|
exports.clone = function(object, deep) {
|
||||||
|
if (Array.isArray(object) && !deep) {
|
||||||
|
return object.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof object === 'object' || Array.isArray(object)) {
|
||||||
|
if (object === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reply = (Array.isArray(object) ? [] : {});
|
||||||
|
for (var key in object) {
|
||||||
|
if (deep && (typeof object[key] === 'object'
|
||||||
|
|| Array.isArray(object[key]))) {
|
||||||
|
reply[key] = exports.clone(object[key], true);
|
||||||
|
} else {
|
||||||
|
reply[key] = object[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object && typeof(object.clone) === 'function') {
|
||||||
|
return object.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
// That leaves numbers, booleans, undefined. Doesn't it?
|
||||||
|
return object;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for extending one object with another
|
||||||
|
* Copies all properties from source to target. Returns the extended target
|
||||||
|
* object.
|
||||||
|
* Taken from John Resig, http://ejohn.org/blog/javascript-getters-and-setters/.
|
||||||
|
*/
|
||||||
|
exports.mixin = function(a, b) {
|
||||||
|
for (var i in b) {
|
||||||
|
var g = b.__lookupGetter__(i);
|
||||||
|
var s = b.__lookupSetter__(i);
|
||||||
|
|
||||||
|
if (g || s) {
|
||||||
|
if (g) {
|
||||||
|
a.__defineGetter__(i, g);
|
||||||
|
}
|
||||||
|
if (s) {
|
||||||
|
a.__defineSetter__(i, s);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
a[i] = b[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basically taken from Sproutcore.
|
||||||
|
* Replaces the count items from idx with objects.
|
||||||
|
*/
|
||||||
|
exports.replace = function(arr, idx, amt, objects) {
|
||||||
|
return arr.slice(0, idx).concat(objects).concat(arr.slice(idx + amt));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the two frames match. You can also pass only points or sizes.
|
||||||
|
* @param r1 {Rect} the first rect
|
||||||
|
* @param r2 {Rect} the second rect
|
||||||
|
* @param delta {Float} an optional delta that allows for rects that do not match exactly. Defaults to 0.1
|
||||||
|
* @returns {Boolean} true if rects match
|
||||||
|
*/
|
||||||
|
exports.rectsEqual = function(r1, r2, delta) {
|
||||||
|
if (!r1 || !r2) {
|
||||||
|
return r1 == r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!delta && delta !== 0) {
|
||||||
|
delta = 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((r1.y != r2.y) && (Math.abs(r1.y - r2.y) > delta)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((r1.x != r2.x) && (Math.abs(r1.x - r2.x) > delta)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((r1.width != r2.width) && (Math.abs(r1.width - r2.width) > delta)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((r1.height != r2.height) && (Math.abs(r1.height - r2.height) > delta)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue