From: Radek Czajka
+ + ( {% trans "pages" %}, {% trans "untagged" %}) +
diff --git a/lib/vstorage/__init__.py b/lib/vstorage/__init__.py index a98f8de3..2708ed79 100644 --- a/lib/vstorage/__init__.py +++ b/lib/vstorage/__init__.py @@ -162,7 +162,7 @@ class VersionedStorage(object): def _file_to_title(self, filename): assert filename.startswith(self.repo_prefix) - name = filename[len(self.repo_prefix):].strip('/').split('.', 1)[0] + name = filename[len(self.repo_prefix):].strip('/').rsplit('.', 1)[0] return urlunquote(name) def __contains__(self, title): diff --git a/redakcja/settings/compress.py b/redakcja/settings/compress.py index bf3e8170..db72c00f 100644 --- a/redakcja/settings/compress.py +++ b/redakcja/settings/compress.py @@ -30,6 +30,7 @@ COMPRESS_JS = { 'js/lib/jquery/jquery.autocomplete.js', 'js/lib/jquery/jquery.blockui.js', 'js/lib/jquery/jquery.elastic.js', + 'js/lib/jquery/jquery.xmlns.js', 'js/button_scripts.js', 'js/slugify.js', diff --git a/redakcja/static/css/html.css b/redakcja/static/css/html.css index 3255d751..0d43611b 100644 --- a/redakcja/static/css/html.css +++ b/redakcja/static/css/html.css @@ -37,7 +37,7 @@ -webkit-border-radius: 4px; } */ -.htmlview *[x-node = 'RDF'] { +.htmlview *[x-node = 'RDF'][x-ns = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'] { display: none; } diff --git a/redakcja/static/js/lib/jquery/jquery.xmlns.js b/redakcja/static/js/lib/jquery/jquery.xmlns.js new file mode 100644 index 00000000..96c5a559 --- /dev/null +++ b/redakcja/static/js/lib/jquery/jquery.xmlns.js @@ -0,0 +1,411 @@ +// +// jquery.xmlns.js: xml-namespace selector support for jQuery +// +// This plugin modifies the jQuery tag and attribute selectors to +// support optional namespace specifiers as defined in CSS 3: +// +// $("elem") - matches 'elem' nodes in the default namespace +// $("|elem") - matches 'elem' nodes that don't have a namespace +// $("NS|elem") - matches 'elem' nodes in declared namespace 'NS' +// $("*|elem") - matches 'elem' nodes in any namespace +// $("NS|*") - matches any nodes in declared namespace 'NS' +// +// A similar synax is also supported for attribute selectors, but note +// that the default namespace does *not* apply to attributes - a missing +// or empty namespace selector selects only attributes with no namespace. +// +// In a slight break from the W3C standards, and with a nod to ease of +// implementation, the empty namespace URI is treated as equivalent to +// an unspecified namespace. Plenty of browsers seem to make the same +// assumption... +// +// Namespace declarations live in the $.xmlns object, which is a simple +// mapping from namespace ids to namespace URIs. The default namespace +// is determined by the value associated with the empty string. +// +// $.xmlns.D = "DAV:" +// $.xmlns.FOO = "http://www.foo.com/xmlns/foobar" +// $.xmlns[""] = "http://www.example.com/new/default/namespace/" +// +// Unfortunately this is a global setting - I can't find a way to do +// query-object-specific namespaces since the jQuery selector machinery +// is stateless. However, you can use the 'xmlns' function to push and +// pop namespace delcarations with ease: +// +// $().xmlns({D:"DAV:"}) // pushes the DAV: namespace +// $().xmlns("DAV:") // makes DAV: the default namespace +// $().xmlns(false) // pops the namespace we just pushed +// $().xmlns(false) // pops again, returning to defaults +// +// To execute this as a kind of "transaction", pass a function as the +// second argument. It will be executed in the context of the current +// jQuery object: +// +// $().xmlns("DAV:",function() { +// // The default namespace is DAV: within this function, +// // but it is reset to the previous value on exit. +// return this.find("response").each(...); +// }).find("div") +// +// If you pass a string as a function, it will be executed against the +// current jQuery object using find(); i.e. the following will find all +// "href" elements in the "DAV:" namespace: +// +// $().xmlns("DAV:","href") +// +// +// And finally, the legal stuff: +// +// Copyright (c) 2009, Ryan Kelly. +// TAG and ATTR functions derived from jQuery's selector.js. +// Dual licensed under the MIT and GPL licenses. +// http://docs.jquery.com/License +// + +(function($) { + +// Some common default namespaces, that are treated specially by browsers. +// +var default_xmlns = { + "xml": "http://www.w3.org/XML/1998/namespace", + "xmlns": "http://www.w3.org/2000/xmlns/", + "html": "http://www.w3.org/1999/xhtml/" +}; + + +// A reverse mapping for common namespace prefixes. +// +var default_xmlns_rev = {} +for(var k in default_xmlns) { + default_xmlns_rev[default_xmlns[k]] = k; +} + + +// $.xmlns is a mapping from namespace identifiers to namespace URIs. +// The default default-namespace is "*", and we provide some additional +// defaults that are specified in the XML Namespaces standard. +// +$.extend({xmlns: $.extend({},default_xmlns,{"":"*"})}); + + +// jQuery method to push/pop namespace declarations. +// +// If a single argument is specified: +// * if it's a mapping, push those namespaces onto the stack +// * if it's a string, push that as the default namespace +// * if it evaluates to false, pop the latest namespace +// +// If two arguments are specified, the second is executed "transactionally" +// using the namespace declarations found in the first. It can be either a +// a selector string (in which case it is passed to this.find()) or a function +// (in which case it is called in the context of the current jQuery object). +// The given namespace mapping is automatically pushed before executing and +// popped afterwards. +// +var xmlns_stack = []; +$.fn.extend({xmlns: function(nsmap,func) { + if(typeof nsmap == "string") { + nsmap = {"":nsmap}; + } + if(nsmap) { + xmlns_stack.push($.xmlns); + $.xmlns = $.extend({},$.xmlns,nsmap); + if(func !== undefined) { + if(typeof func == "string") { + return this.find(func).xmlns(undefined) + } else { + var self = this; + try { + self = func.call(this); + if(!self) { + self = this; + } + } finally { + self.xmlns(undefined); + } + return self + } + } else { + return this; + } + } else { + $.xmlns = (xmlns_stack ? xmlns_stack.pop() : {}); + return this; + } +}}); + + +// Convert a namespace prefix into a namespace URI, based +// on the delcarations made in $.xmlns. +// +var getNamespaceURI = function(id) { + // No namespace id, use the default. + if(!id) { + return $.xmlns[""]; + } + // Strip the pipe character from the specifier + id = id.substr(0,id.length-1); + // Certain special namespaces aren't mapped to a URI + if(id == "" || id == "*") { + return id; + } + var ns = $.xmlns[id]; + if(typeof(ns) == "undefined") { + throw "Syntax error, undefined namespace prefix '" + id + "'"; + } + return ns; +}; + + +// Update the regex used by $.expr to parse selector components for a +// particular type of selector (e.g. "TAG" or "ATTR"). +// +// This logic is taken straight from the jQuery/Sizzle sources. +// +var setExprMatchRegex = function(type,regex) { + $.expr.match[type] = new RegExp(regex.source + /(?![^\[]*\])(?![^\(]*\))/.source); + if($.expr.leftMatch) { + $.expr.leftMatch[type] = new RegExp(/(^(?:.|\r|\n)*?)/.source + $.expr.match[type].source.replace(/\\(\d+)/g, function(all, num){ + return "\\" + (num - 0 + 1); + })); + } +} + + + +// Modify the TAG match regexp to include optional namespace selector. +// This is basically (namespace|)?(tagname). +// +setExprMatchRegex("TAG",/^((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)))/); + + +// Perform some capability-testing. +// +var div = document.createElement("div"); + +// Sometimes getElementsByTagName("*") will return comment nodes, +// which we will have to remove from the results. +// +var gebtn_yields_comments = false; +div.appendChild(document.createComment("")); +if(div.getElementsByTagName("*").length > 0) { + gebtn_yields_comments = true; +} + +// Some browsers return node.localName in upper case, some in lower case. +// +var localname_is_uppercase = true; +if(div.localName && div.localName == "div") { + localname_is_uppercase = false; +} + +// Allow the testing div to be garbage-collected. +// +div = null; + + +// Modify the TAG find function to account for a namespace selector. +// +$.expr.find.TAG = function(match,context,isXML) { + var ns = getNamespaceURI(match[2]); + var ln = match[3]; + var res; + if(typeof context.getElementsByTagNameNS != "undefined") { + // Easy case - we have getElementsByTagNameNS + res = context.getElementsByTagNameNS(ns,ln); + } else if(typeof context.selectNodes != "undefined") { + // Use xpath if possible (not available on HTML DOM nodes in IE) + if(context.ownerDocument) { + context.ownerDocument.setProperty("SelectionLanguage","XPath"); + } else { + context.setProperty("SelectionLanguage","XPath"); + } + var predicate = ""; + if(ns != "*") { + if(ln != "*") { + predicate="namespace-uri()='"+ns+"' and local-name()='"+ln+"'"; + } else { + predicate="namespace-uri()='"+ns+"'"; + } + } else { + if(ln != "*") { + predicate="local-name()='"+ln+"'"; + } + } + if(predicate) { + res = context.selectNodes("descendant-or-self::*["+predicate+"]"); + } else { + res = context.selectNodes("descendant-or-self::*"); + } + } else { + // Otherwise, we need to simulate using getElementsByTagName + res = context.getElementsByTagName(ln); + if(gebtn_yields_comments && ln == "*") { + var tmp = []; + for(var i=0; res[i]; i++) { + if(res[i].nodeType == 1) { + tmp.push(res[i]); + } + } + res = tmp; + } + if(res && ns != "*") { + var tmp = []; + for(var i=0; res[i]; i++) { + if(res[i].namespaceURI == ns || res[i].tagUrn == ns) { + tmp.push(res[i]); + } + } + res = tmp; + } + } + return res; +}; + + +// Check whether a node is part of an XML document. +// Copied verbatim from jQuery sources, needed in TAG preFilter below. +// +var isXML = function(elem){ + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML"; +}; + + +// Modify the TAG preFilter function to work with modified match regexp. +// This normalises case of the tag name if we're in a HTML document. +// +$.expr.preFilter.TAG = function(match, curLoop, inplace, result, not, isXML) { + var ln = match[3]; + if(!isXML) { + if(localname_is_uppercase) { + ln = ln.toUpperCase(); + } else { + ln = ln.toLowerCase(); + } + } + return [match[0],getNamespaceURI(match[2]),ln]; +}; + + +// Modify the TAG filter function to account for a namespace selector. +// +$.expr.filter.TAG = function(elem,match) { + var ns = match[1]; + var ln = match[2]; + var e_ns = elem.namespaceURI ? elem.namespaceURI : elem.tagUrn; + var e_ln = elem.localName ? elem.localName : elem.tagName; + if(ns == "*" || e_ns == ns || (ns == "" && !e_ns)) { + return ((ln == "*" && elem.nodeType == 1) || e_ln == ln); + } + return false; +}; + + +// Modify the ATTR match regexp to extract a namespace selector. +// This is basically ([namespace|])(attrname)(op)(quote)(pattern)(quote) +// +setExprMatchRegex("ATTR",/\[\s*((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF_-]|\\.)+)))\s*(?:(\S?=)\s*(['"]*)(.*?)\5|)\s*\]/); + + +// Modify the ATTR preFilter function to account for new regexp match groups, +// and normalise the namespace URI. +// +$.expr.preFilter.ATTR = function(match, curLoop, inplace, result, not, isXML) { + var name = match[3].replace(/\\/g, ""); + if(!isXML && $.expr.attrMap[name]) { + match[3] = $.expr.attrMap[name]; + } + if( match[4] == "~=" ) { + match[6] = " " + match[6] + " "; + } + if(!match[2] || match[2] == "|") { + match[2] = ""; + } else { + match[2] = getNamespaceURI(match[2]); + } + return match; +}; + + +// Modify the ATTR filter function to account for namespace selector. +// Unfortunately this means factoring out the attribute-checking code +// into a separate function, since it might be called multiple times. +// +var filter_attr = function(result,type,check) { + var value = result + ""; + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value != check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0,check.length+1)===check+"-" : + false; +} + + +$.expr.filter.ATTR = function(elem, match) { + var ns = match[2]; + var name = match[3]; + var type = match[4]; + var check = match[6]; + var result; + // No namespace, just use ordinary attribute lookup. + if(ns == "") { + result = $.expr.attrHandle[name] ? + $.expr.attrHandle[name](elem) : + elem[name] != null ? + elem[name] : + elem.getAttribute(name); + return filter_attr(result,type,check); + } + // Directly use getAttributeNS if applicable and available + if(ns != "*" && typeof elem.getAttributeNS != "undefined") { + return filter_attr(elem.getAttributeNS(ns,name),type,check); + } + // Need to iterate over all attributes, either because we couldn't + // look it up or because we need to match all namespaces. + var attrs = elem.attributes; + for(var i=0; attrs[i]; i++) { + var ln = attrs[i].localName; + if(!ln) { + ln = attrs[i].nodeName + var idx = ln.indexOf(":"); + if(idx >= 0) { + ln = ln.substr(idx+1); + } + } + if(ln == name) { + result = attrs[i].nodeValue; + if(ns == "*" || attrs[i].namespaceURI == ns) { + if(filter_attr(result,type,check)) { + return true; + } + } + if(attrs[i].namespaceURI === "" && attrs[i].prefix) { + if(attrs[i].prefix == default_xmlns_rev[ns]) { + if(filter_attr(result,type,check)) { + return true; + } + } + } + } + } + return false; +}; + + +})(jQuery); + diff --git a/redakcja/static/js/wiki/view_summary.js b/redakcja/static/js/wiki/view_summary.js index 811096dd..b16c288f 100644 --- a/redakcja/static/js/wiki/view_summary.js +++ b/redakcja/static/js/wiki/view_summary.js @@ -29,12 +29,28 @@ SummaryPerspective.prototype = new $.wiki.Perspective(); + SummaryPerspective.prototype.showCharCount = function() { + var cc; + try { + cc = this.doc.getLength(); + $('#charcount_untagged').hide(); + } + catch (e) { + $('#charcount_untagged').show(); + cc = this.doc.text.replace(/\s{2,}/g, ' ').length; + } + $('#charcount').html(cc); + $('#charcount_pages').html((Math.round(cc/18)/100).toLocaleString()); + } + SummaryPerspective.prototype.freezeState = function(){ // must }; SummaryPerspective.prototype.onEnter = function(success, failure){ $.wiki.Perspective.prototype.onEnter.call(this); + + this.showCharCount(); console.log("Entered summery view"); }; diff --git a/redakcja/static/js/wiki/wikiapi.js b/redakcja/static/js/wiki/wikiapi.js index 3907e8e2..c8b36279 100644 --- a/redakcja/static/js/wiki/wikiapi.js +++ b/redakcja/static/js/wiki/wikiapi.js @@ -398,5 +398,22 @@ }); }; + WikiDocument.prototype.getLength = function(params) { + var xml = this.text.replace(/\/(\s+)/g, '