weave-identity/tools/docs/docs.js

365 строки
11 KiB
JavaScript

/* ======* 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 Ubiquity.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Atul Varma <atul@mozilla.com>
* Sander Dijkhuis <sander.dijkhuis@gmail.com>
* Alberto Santini <albertosantini@gmail.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 ======* */
// = App =
//
// This is the application that processes the code and lets the user
// navigate through and read the documentation.
var App = {
};
// FIXME: use jQuery instead.
App.trim = function trim(str) {
return str.replace(/^\s+|\s+$/g,"");
};
// == Page processing ==
// === {{{ App.processors }}} ===
//
// An array of user-defined processor functions. They should take one
// argument, the DOM node containing the documentation. User-defined
// processor functions are called after standard processing is done.
App.processors = [];
// === {{{ App.processCode() }}} ===
//
// Splits {{{code}}} in documented blocks and puts them in {{{div}}}.
// The used structure for each block is:
// {{{
// <div class="documentation"> (...) </div>
// <pre class="code prettyprint"> (...) </pre>
// <div class="divider"/>
// }}}
// Documentation is parsed using [[http://wikicreole.org/|Creole]].
App.processCode = function processCode(code, div) {
var lines = code.replace(/\r\n/g,'\n').replace(/\r/g,'\n').split('\n');
var blocks = [];
var blockText = "";
var codeText = "";
var firstCommentLine;
var lastCommentLine;
function maybeAppendBlock() {
if (blockText)
blocks.push({text: blockText,
lineno: firstCommentLine,
numLines: lastCommentLine - firstCommentLine + 1,
code: codeText});
}
jQuery.each(
lines,
function(lineNum) {
var line = this;
var isCode = true;
var isComment = (App.trim(line).indexOf("//") === 0);
if (isComment) {
var startIndex = line.indexOf("//");
var text = App.trim(line.slice(startIndex + 3));
if (lineNum === lastCommentLine + 1) {
blockText += text + "\n";
lastCommentLine += 1;
isCode = false;
} else if (text.charAt(0) === "=" || text.charAt(0) === "*") {
maybeAppendBlock();
firstCommentLine = lineNum;
lastCommentLine = lineNum;
blockText = text + "\n";
codeText = "";
isCode = false;
}
}
if (isCode)
codeText += line + "\r\n";
});
maybeAppendBlock();
var creole = new Parse.Simple.Creole(
{
forIE: document.all,
interwiki: {
WikiCreole: 'http://www.wikicreole.org/wiki/',
Wikipedia: 'http://en.wikipedia.org/wiki/'
},
linkFormat: ''
});
jQuery.each(
blocks,
function(i) {
var docs = $('<div class="documentation">');
$(docs).css(App.columnCss);
creole.parse(docs.get(0), this.text);
$(div).append(docs);
var code = $('<pre class="code prettyprint">');
$(code).css(App.columnCss);
code.text(this.code);
$(div).append(code);
// Make sure the block ends with a blank line to make it high enough.
// For IE8 an extra space is needed, because otherwise the \n is ignored.
// FIXME: This doesn't fix issue 13 in IE7 yet.
code.append('\n ');
var docsSurplus = docs.height() - code.height() + 1;
if (docsSurplus > 0)
code.css({paddingBottom: docsSurplus + "px"});
$(div).append('<div class="divider">');
});
// Run the user-defined processors.
jQuery.each(
App.processors,
function(i) {
App.processors[i]($(div).find(".documentation"));
});
};
// === {{{ App.updateToC() }}} ===
App.updateToC = function updateToC() {
var headings = $("h2, h3");
var toc = "<p>On this page: <ol>";
var lastLevel = 1;
if (!headings)
$("#toc").html("");
jQuery.each(
headings,
function(i) {
var anchor = App.currentPage + "#" + i;
var level = headings[i].tagName.slice(1);
// FIXME: look for other ancestors too.
if ($(headings[i]).parent().css("display") == "none")
return;
while (level > lastLevel) {
toc += "<ol>";
lastLevel++;
}
while (level < lastLevel) {
toc += "</ol>";
lastLevel--;
}
toc += "<li><a href='#" + anchor + "'>"
+ jQuery.trim($(headings[i]).text()) + "</a>";
$(headings[i]).prepend("<a name='" + anchor + "'></a>");
});
toc += "</ol>";
$("#toc").html(toc);
};
// == Context menus ==
App.menuItems = {}; // Has a {label, urlOrCallback} dict for each keyword.
// === {{{ App.addMenuItem() }}} ===
//
// Adds a menu item to the {{{element}}} DOM node showing the {{{label}}}
// text. If {{{urlOrCallback}}} is an URL, choosing the item causes a new
// window to be opened with that URL. If it's a function, it will be called
// when choosing the item.
//
// If the node does not have a menu yet, one will be created.
App.addMenuItem = function addMenuItem(element, label, urlOrCallback) {
var text = $(element).text();
if (!$(element).parent().hasClass("popup-enabled")) {
App.menuItems[text] = [];
$(element).wrap('<span class="popup-enabled"></span>');
$(element).mousedown(
function(evt) {
evt.preventDefault();
var popup = $('<div class="popup"></div>');
function addItemToPopup(label, urlOrCallback) {
var callback;
var menuItem = $('<div class="item"></div>');
menuItem.text(label);
function onOverOrOut() { $(this).toggleClass("selected"); }
menuItem.mouseover(onOverOrOut);
menuItem.mouseout(onOverOrOut);
if (typeof(urlOrCallback) === "string")
callback = function() {
window.open(urlOrCallback);
};
else
callback = urlOrCallback;
menuItem.mouseup(callback);
popup.append(menuItem);
}
jQuery.each(
App.menuItems[text],
function(i) {
var item = App.menuItems[text][i];
addItemToPopup(item.label, item.urlOrCallback);
});
popup.find(".item:last").addClass("bottom");
popup.css({left: evt.pageX + "px"});
$(window).mouseup(
function mouseup() {
popup.remove();
$(window).unbind("mouseup", mouseup);
});
$(this).append(popup);
});
}
App.menuItems[text].push({ label: label, urlOrCallback: urlOrCallback });
};
// == Navigation ==
App.currentPage = null;
App.pages = {};
// === {{{ App.navigate() }}} ===
//
// Navigates to a different view if needed. The appropriate view is
// fetched from the URL hash. If that is empty, the original page content
// is shown.
App.navigate = function navigate() {
var newPage;
if (window.location.hash)
newPage = window.location.hash.split("#")[1];
else
newPage = "overview";
if (App.currentPage != newPage) {
if (App.currentPage)
$(App.pages[App.currentPage]).hide();
if (!App.pages[newPage]) {
var newDiv = $("<div>");
newDiv.attr("name", newPage);
$("#content").append(newDiv);
App.pages[newPage] = newDiv;
jQuery.get(newPage,
{},
function(code) {
App.processCode(code, newDiv);
App.updateToC();
prettyPrint();
},
"text");
}
$(App.pages[newPage]).show();
App.currentPage = newPage;
}
};
// == Layout ==
App.CHARS_PER_ROW = 80;
// === {{{ App.initColumnSizes() }}} ===
App.initColumnSizes = function initSizes() {
// Get the width of a single monospaced character of code.
var oneCodeCharacter = $('<div class="code">M</div>');
$("#content").append(oneCodeCharacter);
App.charWidth = oneCodeCharacter.width();
App.columnWidth = App.charWidth * App.CHARS_PER_ROW;
$(oneCodeCharacter).remove();
// Dynamically determine the column widths and padding based on
// the font size.
var padding = App.charWidth * 2;
var docWidth = $(".documentation").width();
var magic = 28; // FIXME: without magic, the layout currently breaks.
App.columnCss = {width: App.columnWidth,
paddingLeft: padding,
paddingRight: padding};
$("#content").css({width: App.columnWidth + padding*2 + docWidth + magic});
$(".code").css(App.columnCss);
};
// == Initialisation ==
$(window).ready(
function() {
App.pages["overview"] = $("#overview").get(0);
App.initColumnSizes();
window.setInterval(
function() { App.navigate(); },
100
);
App.navigate();
// **** Add yellow highlighting to double-clicked text.
// Get the selected text in a cross-browser fashion
function getSelectedText(){
if(window.getSelection){
return window.getSelection().toString();
} else if (document.getSelection) {
return document.getSelection();
} else if(document.selection){
return document.selection.createRange().text;
}
}
// Double clicking on a word, it will be yellow highlighted
// in the documentation and code section
$("#content").bind("dblclick", function () {
var text = App.trim(getSelectedText());
if (text) {
jQuery("#content").removeHighlight().highlight(text);
}
});
});