Commit 8fce7066 by Bridger Maxwell

You can now edit a circuit directly on the edit page of the wiki. These edits…

You can now edit a circuit directly on the edit page of the wiki. These edits show up in the history. This hasn't been tested much.
parent 3ed5b161
......@@ -30,15 +30,14 @@ class CircuitExtension(markdown.Extension):
md.inlinePatterns.add(name, pattern, "<reference")
def extendMarkdown(self, md, md_globals):
self.add_inline(md, 'circuit', CircuitLink, r'^circuit:(?P<name>[a-zA-Z0-9]*)$')
self.add_inline(md, 'circuit', CircuitLink, r'^circuit-schematic:(?P<data>.*)$')
class CircuitLink(markdown.inlinepatterns.Pattern):
def handleMatch(self, m):
name = m.group('name')
if not name.isalnum():
return etree.fromstring("<div>Circuit name must be alphanumeric</div>")
data = m.group('data')
return etree.fromstring(render_to_string('show_circuit.html', {'name':name}))
##TODO: We need to html escape the data
return etree.fromstring("<input type='hidden' parts='' value='" + data + "' analyses='' class='schematic ctrls'/>")
def makeExtension(configs=None) :
......
.CodeMirror {
line-height: 1em;
font-family: monospace;
}
.CodeMirror-scroll {
overflow: auto;
height: 300px;
/* This is needed to prevent an IE[67] bug where the scrolled content
is visible outside of the scrolling box. */
position: relative;
outline: none;
}
.CodeMirror-gutter {
position: absolute; left: 0; top: 0;
z-index: 10;
background-color: #f7f7f7;
border-right: 1px solid #eee;
min-width: 2em;
height: 100%;
}
.CodeMirror-gutter-text {
color: #aaa;
text-align: right;
padding: .4em .2em .4em .4em;
white-space: pre !important;
}
.CodeMirror-lines {
padding: .4em;
white-space: pre;
}
.CodeMirror pre {
-moz-border-radius: 0;
-webkit-border-radius: 0;
-o-border-radius: 0;
border-radius: 0;
border-width: 0; margin: 0; padding: 0; background: transparent;
font-family: inherit;
font-size: inherit;
padding: 0; margin: 0;
white-space: pre;
word-wrap: normal;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror textarea {
outline: none !important;
}
.CodeMirror pre.CodeMirror-cursor {
z-index: 10;
position: absolute;
visibility: hidden;
border-left: 1px solid black;
border-right:none;
width:0;
}
.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {}
.CodeMirror-focused pre.CodeMirror-cursor {
visibility: visible;
}
div.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* Default theme */
.cm-s-default span.cm-keyword {color: #708;}
.cm-s-default span.cm-atom {color: #219;}
.cm-s-default span.cm-number {color: #164;}
.cm-s-default span.cm-def {color: #00f;}
.cm-s-default span.cm-variable {color: black;}
.cm-s-default span.cm-variable-2 {color: #05a;}
.cm-s-default span.cm-variable-3 {color: #085;}
.cm-s-default span.cm-property {color: black;}
.cm-s-default span.cm-operator {color: black;}
.cm-s-default span.cm-comment {color: #a50;}
.cm-s-default span.cm-string {color: #a11;}
.cm-s-default span.cm-string-2 {color: #f50;}
.cm-s-default span.cm-meta {color: #555;}
.cm-s-default span.cm-error {color: #f00;}
.cm-s-default span.cm-qualifier {color: #555;}
.cm-s-default span.cm-builtin {color: #30a;}
.cm-s-default span.cm-bracket {color: #cc7;}
.cm-s-default span.cm-tag {color: #170;}
.cm-s-default span.cm-attribute {color: #00c;}
.cm-s-default span.cm-header {color: #a0a;}
.cm-s-default span.cm-quote {color: #090;}
.cm-s-default span.cm-hr {color: #999;}
.cm-s-default span.cm-link {color: #00c;}
span.cm-header, span.cm-strong {font-weight: bold;}
span.cm-em {font-style: italic;}
span.cm-emstrong {font-style: italic; font-weight: bold;}
span.cm-link {text-decoration: underline;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
This source diff could not be displayed because it is too large. You can view the blob instead.
$(function(){
$(document).ready(function() {
$("a[rel*=leanModal]").leanModal();
$("body").append('<div id="circuit_editor" class="leanModal_box" style="z-index: 11000; left: 50%; margin-left: -250px; position: absolute; top: 100px; opacity: 1; "><div align="center"> <input class="schematic" height="300" width="400" id="schematic_editor" name="schematic" type="hidden" value=""/> <button type="button" id="circuit_save_btn">save</button></div></div>');
//This is the editor that pops up as a modal
var editorCircuit = $("#schematic_editor").get(0);
//This is the circuit that they last clicked. The one being edited.
var editingCircuit = null;
//Notice we use live, because new circuits can be inserted
$(".schematic_open").live("click", function() {
//Find the new editingCircuit. Transfer its contents to the editorCircuit
editingCircuit = $(this).children("input.schematic").get(0);
editingCircuit.schematic.update_value();
var circuit_so_far = $(editingCircuit).val();
var n = editorCircuit.schematic.components.length;
for (var i = 0; i < n; i++)
editorCircuit.schematic.components[n - 1 - i].remove();
editorCircuit.schematic.load_schematic(circuit_so_far, "");
});
$("#circuit_save_btn").click(function () {
//Take the circuit from the editor and put it back into editingCircuit
editorCircuit.schematic.update_value();
var saving_circuit = $(editorCircuit).val();
var n = editingCircuit.schematic.components.length;
for (var i = 0; i < n; i++)
editingCircuit.schematic.components[n - 1 - i].remove();
editingCircuit.schematic.load_schematic(saving_circuit, "");
if (editingCircuit.codeMirrorLine) {
editingCircuit.codeMirrorLine.text = "circuit-schematic:" + saving_circuit;
}
$(".modal_close").first().click();
});
});
});
CodeMirror.defineMode("mitx_markdown", function(cmCfg, modeCfg) {
var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true });
var header = 'header'
, code = 'comment'
, quote = 'quote'
, list = 'string'
, hr = 'hr'
, linktext = 'link'
, linkhref = 'string'
, em = 'em'
, strong = 'strong'
, emstrong = 'emstrong';
var circuit_formatter = {
creator: function(text) {
var circuit_value = text.match(circuitRE)[1]
//TODO: We need real html escaping here
circuit_value = CodeMirror.htmlEscape(circuit_value);// circuit_value.replace("\"", "'");
var html = "<a href='#circuit_editor' rel='leanModal' class='schematic_open' style='width:400px; '><input type='hidden' parts='' value='" +
circuit_value + "' analyses='' class='schematic ctrls'/></a>";
return html;
},
callback: function(node, line) {
update_schematics();
var schmInput = node.firstChild;
schmInput.codeMirrorLine = line;
$(node).leanModal();
}
};
var hrRE = /^[*-=_]/
, ulRE = /^[*-+]\s+/
, olRE = /^[0-9]+\.\s+/
, headerRE = /^(?:\={3,}|-{3,})$/
, codeRE = /^(k:\t|\s{4,})/
, textRE = /^[^\[*_\\<>`]+/
, circuitRE = /^circuit-schematic:(.*)$/;
function switchInline(stream, state, f) {
state.f = state.inline = f;
return f(stream, state);
}
function switchBlock(stream, state, f) {
state.f = state.block = f;
return f(stream, state);
}
// Blocks
function blockNormal(stream, state) {
var match;
if (stream.match(circuitRE)) {
stream.skipToEnd();
return circuit_formatter;
} else if (stream.match(codeRE)) {
stream.skipToEnd();
return code;
} else if (stream.eatSpace()) {
return null;
} else if (stream.peek() === '#' || stream.match(headerRE)) {
state.header = true;
} else if (stream.eat('>')) {
state.indentation++;
state.quote = true;
} else if (stream.peek() === '[') {
return switchInline(stream, state, footnoteLink);
} else if (hrRE.test(stream.peek())) {
var re = new RegExp('(?:\s*['+stream.peek()+']){3,}$');
if (stream.match(re, true)) {
return hr;
}
} else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) {
state.indentation += match[0].length;
return list;
}
return switchInline(stream, state, state.inline);
}
function htmlBlock(stream, state) {
var style = htmlMode.token(stream, state.htmlState);
if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) {
state.f = inlineNormal;
state.block = blockNormal;
}
return style;
}
// Inline
function getType(state) {
// Set defaults
returnValue = '';
// Strong / Emphasis
if(state.strong){
if(state.em){
returnValue += (returnValue ? ' ' : '') + emstrong;
} else {
returnValue += (returnValue ? ' ' : '') + strong;
}
} else {
if(state.em){
returnValue += (returnValue ? ' ' : '') + em;
}
}
// Header
if(state.header){
returnValue += (returnValue ? ' ' : '') + header;
}
// Quotes
if(state.quote){
returnValue += (returnValue ? ' ' : '') + quote;
}
// Check valud and return
if(!returnValue){
returnValue = null;
}
return returnValue;
}
function handleText(stream, state) {
if (stream.match(textRE, true)) {
return getType(state);
}
return undefined;
}
function inlineNormal(stream, state) {
var style = state.text(stream, state)
if (typeof style !== 'undefined')
return style;
var ch = stream.next();
if (ch === '\\') {
stream.next();
return getType(state);
}
if (ch === '`') {
return switchInline(stream, state, inlineElement(code, '`'));
}
if (ch === '[') {
return switchInline(stream, state, linkText);
}
if (ch === '<' && stream.match(/^\w/, false)) {
stream.backUp(1);
return switchBlock(stream, state, htmlBlock);
}
var t = getType(state);
if (ch === '*' || ch === '_') {
if (stream.eat(ch)) {
return (state.strong = !state.strong) ? getType(state) : t;
}
return (state.em = !state.em) ? getType(state) : t;
}
return getType(state);
}
function linkText(stream, state) {
while (!stream.eol()) {
var ch = stream.next();
if (ch === '\\') stream.next();
if (ch === ']') {
state.inline = state.f = linkHref;
return linktext;
}
}
return linktext;
}
function linkHref(stream, state) {
stream.eatSpace();
var ch = stream.next();
if (ch === '(' || ch === '[') {
return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
}
return 'error';
}
function footnoteLink(stream, state) {
if (stream.match(/^[^\]]*\]:/, true)) {
state.f = footnoteUrl;
return linktext;
}
return switchInline(stream, state, inlineNormal);
}
function footnoteUrl(stream, state) {
stream.eatSpace();
stream.match(/^[^\s]+/, true);
state.f = state.inline = inlineNormal;
return linkhref;
}
function inlineRE(endChar) {
if (!inlineRE[endChar]) {
// match any not-escaped-non-endChar and any escaped char
// then match endChar or eol
inlineRE[endChar] = new RegExp('^(?:[^\\\\\\' + endChar + ']|\\\\.)*(?:\\' + endChar + '|$)');
}
return inlineRE[endChar];
}
function inlineElement(type, endChar, next) {
next = next || inlineNormal;
return function(stream, state) {
stream.match(inlineRE(endChar));
state.inline = state.f = next;
return type;
};
}
return {
startState: function() {
return {
f: blockNormal,
block: blockNormal,
htmlState: htmlMode.startState(),
indentation: 0,
inline: inlineNormal,
text: handleText,
em: false,
strong: false,
header: false,
quote: false
};
},
copyState: function(s) {
return {
f: s.f,
block: s.block,
htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
indentation: s.indentation,
inline: s.inline,
text: s.text,
em: s.em,
strong: s.strong,
header: s.header,
quote: s.quote
};
},
token: function(stream, state) {
if (stream.sol()) {
// Reset EM state
state.em = false;
// Reset STRONG state
state.strong = false;
// Reset state.header
state.header = false;
// Reset state.quote
state.quote = false;
state.f = state.block;
var previousIndentation = state.indentation
, currentIndentation = 0;
while (previousIndentation > 0) {
if (stream.eat(' ')) {
previousIndentation--;
currentIndentation++;
} else if (previousIndentation >= 4 && stream.eat('\t')) {
previousIndentation -= 4;
currentIndentation += 4;
} else {
break;
}
}
state.indentation = currentIndentation;
if (currentIndentation > 0) return null;
}
return state.f(stream, state);
},
getType: getType
};
});
CodeMirror.defineMIME("text/x-markdown", "markdown");
CodeMirror.defineMode("xml", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var Kludges = parserConfig.htmlMode ? {
autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
"meta": true, "col": true, "frame": true, "base": true, "area": true},
doNotIndent: {"pre": true},
allowUnquoted: true,
allowMissing: false
} : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false};
var alignCDATA = parserConfig.alignCDATA;
// Return variables for tokenizers
var tagName, type;
function inText(stream, state) {
function chain(parser) {
state.tokenize = parser;
return parser(stream, state);
}
var ch = stream.next();
if (ch == "<") {
if (stream.eat("!")) {
if (stream.eat("[")) {
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
else return null;
}
else if (stream.match("--")) return chain(inBlock("comment", "-->"));
else if (stream.match("DOCTYPE", true, true)) {
stream.eatWhile(/[\w\._\-]/);
return chain(doctype(1));
}
else return null;
}
else if (stream.eat("?")) {
stream.eatWhile(/[\w\._\-]/);
state.tokenize = inBlock("meta", "?>");
return "meta";
}
else {
type = stream.eat("/") ? "closeTag" : "openTag";
stream.eatSpace();
tagName = "";
var c;
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
state.tokenize = inTag;
return "tag";
}
}
else if (ch == "&") {
var ok;
if (stream.eat("#")) {
if (stream.eat("x")) {
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
} else {
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
}
} else {
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
}
return ok ? "atom" : "error";
}
else {
stream.eatWhile(/[^&<]/);
return null;
}
}
function inTag(stream, state) {
var ch = stream.next();
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
state.tokenize = inText;
type = ch == ">" ? "endTag" : "selfcloseTag";
return "tag";
}
else if (ch == "=") {
type = "equals";
return null;
}
else if (/[\'\"]/.test(ch)) {
state.tokenize = inAttribute(ch);
return state.tokenize(stream, state);
}
else {
stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
return "word";
}
}
function inAttribute(quote) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.next() == quote) {
state.tokenize = inTag;
break;
}
}
return "string";
};
}
function inBlock(style, terminator) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.match(terminator)) {
state.tokenize = inText;
break;
}
stream.next();
}
return style;
};
}
function doctype(depth) {
return function(stream, state) {
var ch;
while ((ch = stream.next()) != null) {
if (ch == "<") {
state.tokenize = doctype(depth + 1);
return state.tokenize(stream, state);
} else if (ch == ">") {
if (depth == 1) {
state.tokenize = inText;
break;
} else {
state.tokenize = doctype(depth - 1);
return state.tokenize(stream, state);
}
}
}
return "meta";
};
}
var curState, setStyle;
function pass() {
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
}
function cont() {
pass.apply(null, arguments);
return true;
}
function pushContext(tagName, startOfLine) {
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
curState.context = {
prev: curState.context,
tagName: tagName,
indent: curState.indented,
startOfLine: startOfLine,
noIndent: noIndent
};
}
function popContext() {
if (curState.context) curState.context = curState.context.prev;
}
function element(type) {
if (type == "openTag") {
curState.tagName = tagName;
return cont(attributes, endtag(curState.startOfLine));
} else if (type == "closeTag") {
var err = false;
if (curState.context) {
err = curState.context.tagName != tagName;
} else {
err = true;
}
if (err) setStyle = "error";
return cont(endclosetag(err));
}
return cont();
}
function endtag(startOfLine) {
return function(type) {
if (type == "selfcloseTag" ||
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
return cont();
if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
return cont();
};
}
function endclosetag(err) {
return function(type) {
if (err) setStyle = "error";
if (type == "endTag") { popContext(); return cont(); }
setStyle = "error";
return cont(arguments.callee);
}
}
function attributes(type) {
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
if (type == "endTag" || type == "selfcloseTag") return pass();
setStyle = "error";
return cont(attributes);
}
function attribute(type) {
if (type == "equals") return cont(attvalue, attributes);
if (!Kludges.allowMissing) setStyle = "error";
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
}
function attvalue(type) {
if (type == "string") return cont(attvaluemaybe);
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
setStyle = "error";
return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
}
function attvaluemaybe(type) {
if (type == "string") return cont(attvaluemaybe);
else return pass();
}
return {
startState: function() {
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
},
token: function(stream, state) {
if (stream.sol()) {
state.startOfLine = true;
state.indented = stream.indentation();
}
if (stream.eatSpace()) return null;
setStyle = type = tagName = null;
var style = state.tokenize(stream, state);
state.type = type;
if ((style || type) && style != "comment") {
curState = state;
while (true) {
var comb = state.cc.pop() || element;
if (comb(type || style)) break;
}
}
state.startOfLine = false;
return setStyle || style;
},
indent: function(state, textAfter, fullLine) {
var context = state.context;
if ((state.tokenize != inTag && state.tokenize != inText) ||
context && context.noIndent)
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
if (context && /^<\//.test(textAfter))
context = context.prev;
while (context && !context.startOfLine)
context = context.prev;
if (context) return context.indent + indentUnit;
else return 0;
},
compareStates: function(a, b) {
if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
if (!ca || !cb) return ca == cb;
if (ca.tagName != cb.tagName) return false;
}
},
electricChars: "/"
};
});
CodeMirror.defineMIME("application/xml", "xml");
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
......@@ -7,7 +7,7 @@
<!-- <link rel="stylesheet" media="print" href="/static/simplewiki/css/base_print.css" /> -->
<!-- <link rel="stylesheet" href="/static/simplewiki/css/autosuggest_inquisitor.css" /> -->
<!-- <link rel="stylesheet" href="/static/css/local.css" type="text/css" media="all" /> -->
<script type="text/javascript" src="/static/simplewiki/js/bsn.AutoSuggest_c_2.0.js"></script>
<script type="text/javascript" src="/static/js/simplewiki/bsn.AutoSuggest_c_2.0.js"></script>
<%!
from django.core.urlresolvers import reverse
......
......@@ -9,22 +9,54 @@
</%block>
<%block name="wiki_head">
<script type="text/javascript" src="${ settings.LIB_URL }CodeMirror/codemirror.js"></script>
<link rel="stylesheet" href="${ settings.LIB_URL }CodeMirror/codemirror.css" />
<script type="text/javascript" src="${ settings.LIB_URL }CodeMirror/xml.js"></script>
<script type="text/javascript" src="${ settings.LIB_URL }CodeMirror/mitx_markdown.js"></script>
<style type="text/css">
.CodeMirror-scroll {
height: 600px;
width: 100%;
}
.CodeMirror {
border: 1px solid black;
text-align: left;
}
</style>
<script>
$(function() {
//Store the initial contents of #id_contents (the wiki body field) so we can compare for unsaved changes
$("#id_contents").data('initial_contents', $("#id_contents").val());
$(function(){
$(document).ready(function() {
var editor = CodeMirror.fromTextArea(document.getElementById("id_contents"), {
mode: 'mitx_markdown',
matchBrackets: true,
theme: "default"
});
//Store the inital contents so we can compare for unsaved changes
var initial_contents = editor.getValue();
window.onbeforeunload = function askConfirm() { //Warn the user before they navigate away
if ( $("#id_contents").val() != $("#id_contents").data('initial_contents') ) {
if ( editor.getValue() != initial_contents ) {
return "You have made changes to the article that have not been saved yet.";
}
};
$("#submit_edit").click(function() {
$("#id_contents").data('initial_contents', $("#id_contents").val());
initial_contents = editor.getValue();
});
});
});
</script>
</%block>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment