/*\ title: $:/core/modules/wiki.js type: application/javascript module-type: wikimethod Extension methods for the $tw.Wiki object Adds the following properties to the wiki object: * `eventListeners` is an array of {filter: , listener: fn} * `changedTiddlers` is a hashmap describing changes to named tiddlers since wiki change events were last dispatched. Each entry is a hashmap containing two fields: modified: true/false deleted: true/false * `changeCount` is a hashmap by tiddler title containing a numerical index that starts at zero and is incremented each time a tiddler is created changed or deleted * `caches` is a hashmap by tiddler title containing a further hashmap of named cache objects. Caches are automatically cleared when a tiddler is modified or deleted * `macros` is a hashmap by macro name containing an object class inheriting from the Macro tree node \*/ (function(){ /*jslint node: true, browser: true */ /*global $tw: false */ "use strict"; /* Get the value of a text reference */ exports.getTextReference = function(textRef,defaultText,currTiddlerTitle) { var tr = this.parseTextReference(textRef), title = tr.title || currTiddlerTitle, field = tr.field || "text", tiddler = this.getTiddler(title); if(tiddler && $tw.utils.hop(tiddler.fields,field)) { return tiddler.fields[field]; } else { return defaultText; } }; exports.setTextReference = function(textRef,value,currTiddlerTitle,isShadow) { var tr = this.parseTextReference(textRef), title,tiddler,fields; // Check if it is a reference to a tiddler if(tr.title && !tr.field) { tiddler = this.getTiddler(tr.title); this.addTiddler(new $tw.Tiddler(tiddler,{title: tr.title,text: value}),isShadow); // Else check for a field reference } else if(tr.field) { title = tr.title || currTiddlerTitle; tiddler = this.getTiddler(title); if(tiddler) { fields = {}; fields[tr.field] = value; this.addTiddler(new $tw.Tiddler(tiddler,fields)); } } }; exports.deleteTextReference = function(textRef,currTiddlerTitle) { var tr = this.parseTextReference(textRef), title,tiddler,fields; // Check if it is a reference to a tiddler if(tr.title && !tr.field) { this.deleteTiddler(tr.title); // Else check for a field reference } else if(tr.field) { title = tr.title || currTiddlerTitle; tiddler = this.getTiddler(title); if(tiddler && $tw.utils.hop(tiddler.fields,tr.field)) { fields = {}; fields[tr.field] = undefined; this.addTiddler(new $tw.Tiddler(tiddler,fields)); } } }; /* Parse a text reference into its constituent parts */ exports.parseTextReference = function(textRef,currTiddlerTitle) { // Look for a metadata field separator var pos = textRef.indexOf("!!"); if(pos !== -1) { if(pos === 0) { return { field: textRef }; } else { return { title: textRef.substring(0,pos - 1), field: textRef.substring(pos + 2) }; } } else { // Otherwise, we've just got a title return { title: textRef }; } }; exports.addEventListener = function(filter,listener) { this.eventListeners = this.eventListeners || []; this.eventListeners.push({ filter: filter, listener: listener }); }; exports.removeEventListener = function(filter,listener) { for(var c=this.eventListeners.length-1; c>=0; c--) { var l = this.eventListeners[c]; if(l.filter === filter && l.listener === listener) { this.eventListeners.splice(c,1); } } }; /* Causes a tiddler to be marked as changed, incrementing the change count, and triggers event handlers. This method should be called after the changes it describes have been made to the wiki.tiddlers[] array. title: Title of tiddler isDeleted: defaults to false (meaning the tiddler has been created or modified), true if the tiddler has been created */ exports.touchTiddler = function(title,isDeleted) { // Record the touch in the list of changed tiddlers this.changedTiddlers = this.changedTiddlers || {}; this.changedTiddlers[title] = this.changedTiddlers[title] || []; this.changedTiddlers[title][isDeleted ? "deleted" : "modified"] = true; // Increment the change count this.changeCount = this.changeCount || {}; if($tw.utils.hop(this.changeCount,title)) { this.changeCount[title]++; } else { this.changeCount[title] = 1; } // Trigger events this.eventListeners = this.eventListeners || []; if(!this.eventsTriggered) { var me = this; $tw.utils.nextTick(function() { var changes = me.changedTiddlers; me.changedTiddlers = {}; me.eventsTriggered = false; for(var e=0; e bb) { return 1; } else { return 0; } } }); for(t=0; t b) { return isDescending ? -1 : +1; } else { return 0; } } }); }; exports.forEachTiddler = function(/* [sortField,[excludeTag,]]callback */) { var arg = 0, sortField = arguments.length > 1 ? arguments[arg++] : null, excludeTag = arguments.length > 2 ? arguments[arg++] : null, callback = arguments[arg++], titles = this.getTiddlers(sortField,excludeTag), t, tiddler; for(t=0; t b.info.priority) { return +1; } else { return 0; } } }); }; /* Invoke the highest priority saver that successfully handles a method */ exports.callSaver = function(method /*, args */ ) { for(var t=this.savers.length-1; t>=0; t--) { var saver = this.savers[t]; if(saver[method].apply(saver,Array.prototype.slice.call(arguments,1))) { return true; } } return false; }; /* Save the wiki contents template: the tiddler containing the template to save downloadType: the content type for the saved file */ exports.saveWiki = function(options) { options = options || {}; var template = options.template || "$:/core/templates/tiddlywiki5.template.html", downloadType = options.downloadType || "text/plain", text = this.renderTiddler(downloadType,template); this.callSaver("save",text); }; })();